diff --git a/hideout-activitypub/build.gradle.kts b/hideout-activitypub/build.gradle.kts new file mode 100644 index 00000000..45be2756 --- /dev/null +++ b/hideout-activitypub/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") version "1.9.23" +} + +group = "dev.usbharu" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/hideout-activitypub/gradle/wrapper/gradle-wrapper.jar b/hideout-activitypub/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..249e5832 Binary files /dev/null and b/hideout-activitypub/gradle/wrapper/gradle-wrapper.jar differ diff --git a/hideout-activitypub/gradle/wrapper/gradle-wrapper.properties b/hideout-activitypub/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..6729f90e --- /dev/null +++ b/hideout-activitypub/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Jun 01 11:47:11 JST 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/hideout-activitypub/gradlew b/hideout-activitypub/gradlew new file mode 100644 index 00000000..1b6c7873 --- /dev/null +++ b/hideout-activitypub/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/hideout-activitypub/gradlew.bat b/hideout-activitypub/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/hideout-activitypub/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/hideout-activitypub/settings.gradle.kts b/hideout-activitypub/settings.gradle.kts new file mode 100644 index 00000000..1fd1691d --- /dev/null +++ b/hideout-activitypub/settings.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" +} +rootProject.name = "hideout-activitypub" + diff --git a/hideout-activitypub/src/main/kotlin/Main.kt b/hideout-activitypub/src/main/kotlin/Main.kt new file mode 100644 index 00000000..27f6ee1a --- /dev/null +++ b/hideout-activitypub/src/main/kotlin/Main.kt @@ -0,0 +1,5 @@ +package dev.usbharu + +fun main() { + println("Hello World!") +} \ No newline at end of file diff --git a/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt b/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt index 8d73d7c6..a93ba706 100644 --- a/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt +++ b/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt @@ -14,7 +14,6 @@ * limitations under the License. */ -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.selectAll import java.net.MalformedURLException diff --git a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt index 6b482f85..d48c72d8 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -17,8 +17,6 @@ package mastodon.account import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.infrastructure.exposedquery.FollowerQueryServiceImpl import dev.usbharu.owl.producer.api.OwlProducer import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest diff --git a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt index 6d809267..f20da72f 100644 --- a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt +++ b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt @@ -18,7 +18,6 @@ package util import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt deleted file mode 100644 index 6c19c683..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain - -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject - -object Constant { - val context = listOf( - StringOrObject("https://www.w3.org/ns/activitystreams"), - StringOrObject("https://w3id.org/security/v1"), - StringOrObject( - mapOf( - "manuallyApprovesFollowers" to "as:manuallyApprovesFollowers", - "sensitive" to "as:sensitive", - "Hashtag" to "as:Hashtag", - "quoteUrl" to "as:quoteUrl", - "toot" to "http://joinmastodon.org/ns#", - "Emoji" to "toot:Emoji", - "featured" to "toot:featured", - "discoverable" to "toot:discoverable", - "schema" to "http://schema.org#", - "PropertyValue" to "schema:PropertyValue", - "value" to "schema:value", - ) - ) - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt deleted file mode 100644 index 8ce12a19..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class ActivityPubProcessException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 5370068873167636639L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt deleted file mode 100644 index d3125395..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class FailedProcessException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -1305337651143409144L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt deleted file mode 100644 index 528277a8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import java.io.Serial - -class FailedToGetActivityPubResourceException : FailedToGetResourcesException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 6420233106776818052L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt deleted file mode 100644 index aa50d3db..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class HttpSignatureUnauthorizedException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -6449793151674654501L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt deleted file mode 100644 index ae84181d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class IllegalActivityPubObjectException : IllegalArgumentException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 7216998115771415263L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt deleted file mode 100644 index 841641cc..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class JsonParseException : IllegalArgumentException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 7975567796830950692L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt deleted file mode 100644 index 84c090cc..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Accept @JsonCreator constructor( - type: List = emptyList(), - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") - val apObject: Object, - override val actor: String -) : Object( - type = add(type, "Accept") -), - HasActor { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Accept - - if (apObject != other.apObject) return false - if (actor != other.actor) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - return result - } - - override fun toString(): String { - return "Accept(" + - "apObject=$apObject, " + - "actor='$actor'" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt deleted file mode 100644 index c07eab7c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonProperty -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Announce @JsonCreator constructor( - type: List = emptyList(), - @JsonProperty("object") - val apObject: String, - override val actor: String, - override val id: String, - val published: String, - val to: List = emptyList(), - val cc: List = emptyList() -) : Object( - type = add(type, "Announce") -), - HasActor, - HasId { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Announce - - if (apObject != other.apObject) return false - if (actor != other.actor) return false - if (id != other.id) return false - if (published != other.published) return false - if (to != other.to) return false - if (cc != other.cc) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + published.hashCode() - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() - return result - } - - override fun toString(): String { - return "Announce(" + - "apObject='$apObject', " + - "actor='$actor', " + - "id='$id', " + - "published='$published', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt deleted file mode 100644 index 53e12cd5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Create( - type: List = emptyList(), - val name: String? = null, - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") - val apObject: Object, - override val actor: String, - override val id: String, - val to: List = emptyList(), - val cc: List = emptyList() -) : Object( - type = add(type, "Create") -), - HasId, - HasActor { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Create - - if (name != other.name) return false - if (apObject != other.apObject) return false - if (actor != other.actor) return false - if (id != other.id) return false - if (to != other.to) return false - if (cc != other.cc) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + (name?.hashCode() ?: 0) - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() - return result - } - - override fun toString(): String { - return "Create(" + - "name=$name, " + - "apObject=$apObject, " + - "actor='$actor', " + - "id='$id', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt deleted file mode 100644 index 43ec1a51..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Delete : Object, HasId, HasActor { - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") - val apObject: Object - val published: String - override var actor: String = "" - override var id: String = "" - - constructor( - type: List = emptyList(), - actor: String, - id: String, - `object`: Object, - published: String - ) : super(add(type, "Delete")) { - this.apObject = `object` - this.published = published - this.actor = actor - this.id = id - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Delete - - if (apObject != other.apObject) return false - if (published != other.published) return false - if (actor != other.actor) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + published.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String { - return "Delete(" + - "apObject=$apObject, " + - "published='$published', " + - "actor='$actor', " + - "id='$id'" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt deleted file mode 100644 index 632fb7cb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonSetter -import com.fasterxml.jackson.annotation.Nulls -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Document( - type: List = emptyList(), - @JsonSetter(nulls = Nulls.AS_EMPTY) - override val name: String = "", - val mediaType: String, - val url: String -) : Object( - type = add(type, "Document") -), - HasName { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Document - - if (mediaType != other.mediaType) return false - if (url != other.url) return false - if (name != other.name) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + mediaType.hashCode() - result = 31 * result + url.hashCode() - result = 31 * result + name.hashCode() - return result - } - - override fun toString(): String = "Document(mediaType=$mediaType, url=$url, name='$name') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt deleted file mode 100644 index f1f7ae7b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Emoji( - type: List, - override val name: String, - override val id: String, - val updated: String, - val icon: Image -) : Object( - type = add(type, "Emoji") -), - HasName, - HasId { - - override fun toString(): String { - return "Emoji(" + - "name='$name', " + - "id='$id', " + - "updated='$updated', " + - "icon=$icon" + - ")" + - " ${super.toString()}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Emoji - - if (name != other.name) return false - if (id != other.id) return false - if (updated != other.updated) return false - if (icon != other.icon) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + updated.hashCode() - result = 31 * result + icon.hashCode() - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt deleted file mode 100644 index f404946a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Follow( - type: List = emptyList(), - @JsonProperty("object") val apObject: String, - override val actor: String, - val id: String? = null -) : Object( - type = add(type, "Follow") -), - HasActor { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Follow - - if (apObject != other.apObject) return false - if (actor != other.actor) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + (id?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Follow(" + - "apObject='$apObject', " + - "actor='$actor', " + - "id=$id" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt deleted file mode 100644 index d04a3a7c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -interface HasActor { - val actor: String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt deleted file mode 100644 index b4043b5c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -interface HasId { - val id: String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt deleted file mode 100644 index a522574c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Image( - type: List = emptyList(), - val mediaType: String? = null, - val url: String -) : Object( - add(type, "Image") -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Image - - if (mediaType != other.mediaType) return false - if (url != other.url) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + (mediaType?.hashCode() ?: 0) - result = 31 * result + url.hashCode() - return result - } - - override fun toString(): String { - return "Image(" + - "mediaType=$mediaType, " + - "url='$url'" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt deleted file mode 100644 index 88a8e48d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonAutoDetect -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import com.fasterxml.jackson.databind.annotation.JsonSerialize - -@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) -open class JsonLd { - @JsonProperty("@context") - @JsonDeserialize(contentUsing = StringOrObjectDeserializer::class) - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = ContextSerializer::class) - @JsonInclude(JsonInclude.Include.NON_EMPTY) - var context: List = emptyList() - set(value) { - field = value.filterNot { it.isEmpty() } - } - - @JsonCreator - constructor(context: List?) { - if (context != null) { - this.context = context.filterNotNull().filterNot { it.isEmpty() } - } else { - this.context = emptyList() - } - } - - protected constructor() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is JsonLd) return false - - return context == other.context - } - - override fun hashCode(): Int = context.hashCode() - - override fun toString(): String = "JsonLd(context=$context)" -} - -class ContextDeserializer : JsonDeserializer() { - - override fun deserialize( - p0: com.fasterxml.jackson.core.JsonParser?, - p1: com.fasterxml.jackson.databind.DeserializationContext? - ): String { - val readTree: JsonNode = p0?.codec?.readTree(p0) ?: return "" - if (readTree.isValueNode) { - return readTree.textValue() - } - return "" - } -} - -class ContextSerializer : JsonSerializer>() { - - @Deprecated("Deprecated in Java") - override fun isEmpty(value: List?): Boolean = value.isNullOrEmpty() - - override fun isEmpty(provider: SerializerProvider?, value: List?): Boolean = value.isNullOrEmpty() - - override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider) { - if (value.isNullOrEmpty()) { - serializers.defaultSerializeNull(gen) - return - } - if (value.size == 1) { - serializers.findValueSerializer(StringOrObject::class.java).serialize(value[0], gen, serializers) - } else { - gen?.writeStartArray() - value.forEach { - serializers.findValueSerializer(StringOrObject::class.java).serialize(it, gen, serializers) - } - gen?.writeEndArray() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt deleted file mode 100644 index bb046d56..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Key( - override val id: String, - val owner: String, - val publicKeyPem: String -) : Object( - type = add(list = emptyList(), type = "Key") -), - HasId { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Key - - if (owner != other.owner) return false - if (publicKeyPem != other.publicKeyPem) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + owner.hashCode() - result = 31 * result + publicKeyPem.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String = "Key(owner=$owner, publicKeyPem=$publicKeyPem, id='$id') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt deleted file mode 100644 index 6b46cb54..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Like( - type: List = emptyList(), - override val actor: String, - override val id: String, - @JsonProperty("object") val apObject: String, - val content: String, - @JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List = emptyList() -) : Object( - type = add(type, "Like") -), - HasId, - HasActor { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Like - - if (actor != other.actor) return false - if (id != other.id) return false - if (apObject != other.apObject) return false - if (content != other.content) return false - if (tag != other.tag) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + content.hashCode() - result = 31 * result + tag.hashCode() - return result - } - - override fun toString(): String { - return "Like(" + - "actor='$actor', " + - "id='$id', " + - "apObject='$apObject', " + - "content='$content', " + - "tag=$tag" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt deleted file mode 100644 index b925c861..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Note -@Suppress("LongParameterList", "CyclomaticComplexMethod") -constructor( - type: List = emptyList(), - override val id: String, - val attributedTo: String, - val content: String, - val published: String, - val to: List = emptyList(), - val cc: List = emptyList(), - val sensitive: Boolean = false, - val inReplyTo: String? = null, - val attachment: List = emptyList(), - @JsonDeserialize(contentUsing = ObjectDeserializer::class) - val tag: List = emptyList(), - val quoteUri: String? = null, - val quoteUrl: String? = null, - @JsonProperty("_misskey_quote") - val misskeyQuote: String? = null -) : Object( - type = add(type, "Note") -), - HasId { - - @Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod") - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Note - - if (id != other.id) return false - if (attributedTo != other.attributedTo) return false - if (content != other.content) return false - if (published != other.published) return false - if (to != other.to) return false - if (cc != other.cc) return false - if (sensitive != other.sensitive) return false - if (inReplyTo != other.inReplyTo) return false - if (attachment != other.attachment) return false - if (tag != other.tag) return false - if (quoteUri != other.quoteUri) return false - if (quoteUrl != other.quoteUrl) return false - if (misskeyQuote != other.misskeyQuote) return false - - return true - } - - @Suppress("CyclomaticComplexMethod") - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + attributedTo.hashCode() - result = 31 * result + content.hashCode() - result = 31 * result + published.hashCode() - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() - result = 31 * result + sensitive.hashCode() - result = 31 * result + (inReplyTo?.hashCode() ?: 0) - result = 31 * result + attachment.hashCode() - result = 31 * result + tag.hashCode() - result = 31 * result + (quoteUri?.hashCode() ?: 0) - result = 31 * result + (quoteUrl?.hashCode() ?: 0) - result = 31 * result + (misskeyQuote?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Note(" + - "id='$id', " + - "attributedTo='$attributedTo', " + - "content='$content', " + - "published='$published', " + - "to=$to, " + - "cc=$cc, " + - "sensitive=$sensitive, " + - "inReplyTo=$inReplyTo, " + - "attachment=$attachment, " + - "tag=$tag, " + - "quoteUri=$quoteUri, " + - "quoteUrl=$quoteUrl, " + - "misskeyQuote=$misskeyQuote" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt deleted file mode 100644 index 3f4a7dbe..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Person -@Suppress("LongParameterList") -constructor( - type: List = emptyList(), - val name: String?, - override val id: String, - var preferredUsername: String, - var summary: String?, - var inbox: String, - var outbox: String, - var url: String, - private var icon: Image?, - var publicKey: Key, - var endpoints: Map = emptyMap(), - var followers: String?, - var following: String?, - val manuallyApprovesFollowers: Boolean? = false -) : Object(add(type, "Person")), HasId { - - @Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod") - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Person - - if (name != other.name) return false - if (id != other.id) return false - if (preferredUsername != other.preferredUsername) return false - if (summary != other.summary) return false - if (inbox != other.inbox) return false - if (outbox != other.outbox) return false - if (url != other.url) return false - if (icon != other.icon) return false - if (publicKey != other.publicKey) return false - if (endpoints != other.endpoints) return false - if (followers != other.followers) return false - if (following != other.following) return false - if (manuallyApprovesFollowers != other.manuallyApprovesFollowers) return false - - return true - } - - @Suppress("CyclomaticComplexMethod") - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + (name?.hashCode() ?: 0) - result = 31 * result + id.hashCode() - result = 31 * result + preferredUsername.hashCode() - result = 31 * result + (summary?.hashCode() ?: 0) - result = 31 * result + inbox.hashCode() - result = 31 * result + outbox.hashCode() - result = 31 * result + url.hashCode() - result = 31 * result + (icon?.hashCode() ?: 0) - result = 31 * result + publicKey.hashCode() - result = 31 * result + endpoints.hashCode() - result = 31 * result + (followers?.hashCode() ?: 0) - result = 31 * result + (following?.hashCode() ?: 0) - result = 31 * result + (manuallyApprovesFollowers?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Person(" + - "name=$name, " + - "id='$id', " + - "preferredUsername='$preferredUsername', " + - "summary=$summary, " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "icon=$icon, " + - "publicKey=$publicKey, " + - "endpoints=$endpoints, " + - "followers=$followers, " + - "following=$following, " + - "manuallyApprovesFollowers=$manuallyApprovesFollowers" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt deleted file mode 100644 index 5216c2a1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Reject( - override val actor: String, - override val id: String, - @JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object -) : Object(listOf("Reject")), HasId, HasActor { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Reject - - if (actor != other.actor) return false - if (id != other.id) return false - if (apObject != other.apObject) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + apObject.hashCode() - return result - } - - override fun toString(): String { - return "Reject(" + - "actor='$actor', " + - "id='$id', " + - "apObject=$apObject" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt deleted file mode 100644 index 3a9969db..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt +++ /dev/null @@ -1,76 +0,0 @@ -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.databind.* - -open class StringOrObject { - var contextString: String? = null - var contextObject: Map? = null - - @JsonCreator - protected constructor() - - constructor(string: String) : this() { - contextString = string - } - - constructor(contextObject: Map) : this() { - this.contextObject = contextObject - } - - fun isEmpty(): Boolean = contextString.isNullOrEmpty() and contextObject.isNullOrEmpty() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as StringOrObject - - if (contextString != other.contextString) return false - if (contextObject != other.contextObject) return false - - return true - } - - override fun hashCode(): Int { - var result = contextString?.hashCode() ?: 0 - result = 31 * result + (contextObject?.hashCode() ?: 0) - return result - } - - override fun toString(): String = "StringOrObject(contextString=$contextString, contextObject=$contextObject)" -} - -class StringOrObjectDeserializer : JsonDeserializer() { - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext): StringOrObject { - val readTree: JsonNode = p?.codec?.readTree(p) ?: return StringOrObject("") - return if (readTree.isValueNode) { - StringOrObject(readTree.textValue()) - } else if (readTree.isObject) { - val map: Map = ctxt.readTreeAsValue( - readTree, - ctxt.typeFactory.constructType(object : TypeReference>() {}) - ) - StringOrObject(map) - } else { - StringOrObject("") - } - } -} - -class StringORObjectSerializer : JsonSerializer() { - override fun serialize(value: StringOrObject?, gen: JsonGenerator?, serializers: SerializerProvider) { - if (value == null) { - serializers.defaultSerializeNull(gen) - return - } - if (value.contextString != null) { - gen?.writeString(value.contextString) - } else { - serializers.defaultSerializeValue(value.contextObject, gen) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt deleted file mode 100644 index 56ea7cde..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Tombstone(type: List = emptyList(), override val id: String) : - Object(add(type, "Tombstone")), - HasId { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Tombstone - - return id == other.id - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String = "Tombstone(id='$id') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt deleted file mode 100644 index 038d4054..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Undo( - type: List = emptyList(), - override val actor: String, - override val id: String, - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") val apObject: Object, - val published: String? -) : Object(add(type, "Undo")), HasId, HasActor { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Undo - - if (actor != other.actor) return false - if (id != other.id) return false - if (apObject != other.apObject) return false - if (published != other.published) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + (published?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Undo(" + - "actor='$actor', " + - "id='$id', " + - "apObject=$apObject, " + - "published=$published" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt deleted file mode 100644 index 2e3cbf1f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.nodeinfo - -data class Nodeinfo( - val links: List -) { - data class Links( - val rel: String, - val href: String - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt deleted file mode 100644 index f65749ef..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("ClassName") - -package dev.usbharu.hideout.activitypub.domain.model.nodeinfo - -@Suppress("ClassNaming") -data class Nodeinfo2_0( - val version: String, - val software: Software, - val protocols: List, - val services: Services, - val openRegistrations: Boolean, - val usage: Usage, - val metadata: Metadata -) { - data class Software( - val name: String, - val version: String - ) - - data class Services( - val inbound: List, - val outbound: List - ) - - data class Usage( - val users: Users, - val localPosts: Int, - val localComments: Int - ) { - data class Users( - val total: Int, - val activeHalfYear: Int, - val activeMonth: Int - ) - } - - data class Metadata( - val nodeName: String, - val nodeDescription: String, - val maintainer: Maintainer, - val langs: List, - val tosUrl: String, - val repositoryUrl: String, - val feedbackUrl: String, - ) { - data class Maintainer( - val name: String, - val email: String - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt deleted file mode 100644 index c40b37fb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.annotation.JsonSerialize -import dev.usbharu.hideout.activitypub.domain.model.JsonLd - -open class Object : JsonLd { - @JsonSerialize(using = TypeSerializer::class) - var type: List = emptyList() - set(value) { - field = value.filter { it.isNotBlank() } - } - - protected constructor() - constructor(type: List) : super() { - this.type = type.filter { it.isNotBlank() } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Object - - return type == other.type - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + type.hashCode() - return result - } - - override fun toString(): String = "Object(type=$type) ${super.toString()}" - - companion object { - @JvmStatic - protected fun add(list: List, type: String): List { - val toMutableList = list.toMutableList() - toMutableList.add(type) - return toMutableList.distinct() - } - } -} - -class TypeSerializer : JsonSerializer>() { - override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider?) { - if (value?.size == 1) { - gen?.writeString(value[0]) - } else { - gen?.writeStartArray() - value?.forEach { - gen?.writeString(it) - } - gen?.writeEndArray() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt deleted file mode 100644 index 1a554745..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import dev.usbharu.hideout.activitypub.domain.model.* -import dev.usbharu.hideout.activitypub.service.common.ExtendedActivityVocabulary - -class ObjectDeserializer : JsonDeserializer() { - @Suppress("LongMethod", "CyclomaticComplexMethod") - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object? { - requireNotNull(p) - val treeNode: JsonNode = requireNotNull(p.codec?.readTree(p)) - if (treeNode.isValueNode) { - return ObjectValue( - emptyList(), - treeNode.asText() - ) - } else if (treeNode.isObject) { - val type = treeNode["type"] - val activityType = if (type.isArray) { - type.firstNotNullOf { jsonNode: JsonNode -> - ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(jsonNode.asText(), true) } - } - } else if (type.isValueNode) { - ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(type.asText(), true) } - } else { - null - } - - return when (activityType) { - ExtendedActivityVocabulary.Follow -> p.codec.treeToValue(treeNode, Follow::class.java) - ExtendedActivityVocabulary.Note -> p.codec.treeToValue(treeNode, Note::class.java) - ExtendedActivityVocabulary.Object -> p.codec.treeToValue(treeNode, Object::class.java) - ExtendedActivityVocabulary.Link -> null - ExtendedActivityVocabulary.Activity -> null - ExtendedActivityVocabulary.IntransitiveActivity -> null - ExtendedActivityVocabulary.Collection -> null - ExtendedActivityVocabulary.OrderedCollection -> null - ExtendedActivityVocabulary.CollectionPage -> null - ExtendedActivityVocabulary.OrderedCollectionPage -> null - ExtendedActivityVocabulary.Accept -> p.codec.treeToValue(treeNode, Accept::class.java) - ExtendedActivityVocabulary.Add -> null - ExtendedActivityVocabulary.Announce -> p.codec.treeToValue(treeNode, Announce::class.java) - ExtendedActivityVocabulary.Arrive -> null - ExtendedActivityVocabulary.Block -> null - ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java) - ExtendedActivityVocabulary.Delete -> p.codec.treeToValue(treeNode, Delete::class.java) - ExtendedActivityVocabulary.Dislike -> null - ExtendedActivityVocabulary.Flag -> null - ExtendedActivityVocabulary.Ignore -> null - ExtendedActivityVocabulary.Invite -> null - ExtendedActivityVocabulary.Join -> null - ExtendedActivityVocabulary.Leave -> null - ExtendedActivityVocabulary.Like -> p.codec.treeToValue(treeNode, Like::class.java) - ExtendedActivityVocabulary.Listen -> null - ExtendedActivityVocabulary.Move -> null - ExtendedActivityVocabulary.Offer -> null - ExtendedActivityVocabulary.Question -> null - ExtendedActivityVocabulary.Reject -> p.codec.treeToValue(treeNode, Reject::class.java) - ExtendedActivityVocabulary.Read -> null - ExtendedActivityVocabulary.Remove -> null - ExtendedActivityVocabulary.TentativeReject -> null - ExtendedActivityVocabulary.TentativeAccept -> null - ExtendedActivityVocabulary.Travel -> null - ExtendedActivityVocabulary.Undo -> p.codec.treeToValue(treeNode, Undo::class.java) - ExtendedActivityVocabulary.Update -> null - ExtendedActivityVocabulary.View -> null - ExtendedActivityVocabulary.Application -> null - ExtendedActivityVocabulary.Group -> null - ExtendedActivityVocabulary.Organization -> null - ExtendedActivityVocabulary.Person -> p.codec.treeToValue(treeNode, Person::class.java) - ExtendedActivityVocabulary.Service -> null - ExtendedActivityVocabulary.Article -> null - ExtendedActivityVocabulary.Audio -> null - ExtendedActivityVocabulary.Document -> p.codec.treeToValue(treeNode, Document::class.java) - ExtendedActivityVocabulary.Event -> null - ExtendedActivityVocabulary.Image -> p.codec.treeToValue(treeNode, Image::class.java) - ExtendedActivityVocabulary.Page -> null - ExtendedActivityVocabulary.Place -> null - ExtendedActivityVocabulary.Profile -> null - ExtendedActivityVocabulary.Relationship -> null - ExtendedActivityVocabulary.Tombstone -> p.codec.treeToValue(treeNode, Tombstone::class.java) - ExtendedActivityVocabulary.Video -> null - ExtendedActivityVocabulary.Mention -> null - ExtendedActivityVocabulary.Emoji -> p.codec.treeToValue(treeNode, Emoji::class.java) - null -> null - } - } else { - return null - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt deleted file mode 100644 index 6bb2c9d5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.annotation.JsonCreator - -@Suppress("VariableNaming") -open class ObjectValue @JsonCreator constructor(type: List, var `object`: String) : Object( - type -) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as ObjectValue - - return `object` == other.`object` - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + `object`.hashCode() - return result - } - - override fun toString(): String = "ObjectValue(`object`='$`object`') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt deleted file mode 100644 index 3760a991..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.webfinger - -data class WebFinger(val subject: String, val links: List) { - data class Link(val rel: String, val type: String, val href: String) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt deleted file mode 100644 index 7807131d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.infrastructure.exposedquery - -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.activitypub.query.AnnounceQueryService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.springframework.stereotype.Repository -import java.time.Instant - -@Repository -class ExposedAnnounceQueryService( - private val postRepository: PostRepository, - private val postResultRowMapper: ResultRowMapper -) : AnnounceQueryService { - override suspend fun findById(id: Long): Pair? { - return Posts - .leftJoin(Actors) - .selectAll().where { Posts.id eq id } - .singleOrNull() - ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } - } - - override suspend fun findByApId(apId: String): Pair? { - return Posts - .leftJoin(Actors) - .selectAll().where { Posts.apId eq apId } - .singleOrNull() - ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } - } - - private suspend fun ResultRow.toAnnounce(): Announce? { - val repostId = this[Posts.repostId] ?: return null - val repost = postRepository.findById(repostId)?.url ?: return null - - val (to, cc) = visibility( - Visibility.entries.first { visibility -> visibility.ordinal == this[Posts.visibility] }, - this[Actors.followers] - ) - - return Announce( - type = emptyList(), - id = this[Posts.apId], - apObject = repost, - actor = this[Actors.url], - published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(), - to = to, - cc = cc - ) - } - - private fun visibility(visibility: Visibility, followers: String?): Pair, List> { - return when (visibility) { - Visibility.PUBLIC -> listOf(APNoteServiceImpl.public) to listOf(APNoteServiceImpl.public) - Visibility.UNLISTED -> listOfNotNull(followers) to listOf(APNoteServiceImpl.public) - Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers) - Visibility.DIRECT -> TODO() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt deleted file mode 100644 index 85ee76ae..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.infrastructure.exposedquery - -import dev.usbharu.hideout.activitypub.domain.model.Document -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.exposedrepository.* -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository -import java.time.Instant - -@Repository -class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper) : - NoteQueryService { - override suspend fun findById(id: Long): Pair? { - return Posts - .leftJoin(Actors) - .leftJoin(PostsMedia) - .leftJoin(Media) - .selectAll().where { Posts.id eq id } - .let { - (it.toNote() ?: return null) to ( - postQueryMapper.map(it) - .singleOrNull() ?: return null - ) - } - } - - override suspend fun findByApid(apId: String): Pair? { - return Posts - .leftJoin(Actors) - .leftJoin(PostsMedia) - .leftJoin(Media) - .selectAll().where { Posts.apId eq apId } - .let { - (it.toNote() ?: return null) to ( - postQueryMapper.map(it) - .singleOrNull() ?: return null - ) - } - } - - private suspend fun ResultRow.toNote(mediaList: List): Note { - val replyId = this[Posts.replyId] - val replyTo = if (replyId != null) { - val url = postRepository.findById(replyId)?.url - if (url == null) { - logger.warn("Failed to get replyId: $replyId") - } - url - } else { - null - } - - val repostId = this[Posts.repostId] - val repost = if (repostId != null) { - val url = postRepository.findById(repostId)?.url - if (url == null) { - logger.warn("Failed to get repostId: $repostId") - } - url - } else { - null - } - - val visibility1 = - visibility( - Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, - this[Actors.followers] - ) - return Note( - id = this[Posts.apId], - attributedTo = this[Actors.url], - content = this[Posts.text], - published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(), - to = visibility1.first, - cc = visibility1.second, - inReplyTo = replyTo, - misskeyQuote = repost, - quoteUri = repost, - quoteUrl = repost, - sensitive = this[Posts.sensitive], - attachment = mediaList.map { Document(url = it.url, mediaType = "image/jpeg") } - ) - } - - private suspend fun Query.toNote(): Note? { - return this.groupBy { it[Posts.id] } - .map { it.value } - .map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) } - .singleOrNull() - } - - private fun visibility(visibility: Visibility, followers: String?): Pair, List> { - return when (visibility) { - Visibility.PUBLIC -> listOf(public) to listOf(public) - Visibility.UNLISTED -> listOfNotNull(followers) to listOf(public) - Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers) - Visibility.DIRECT -> TODO() - } - } - - companion object { - private val logger = LoggerFactory.getLogger(NoteQueryServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt deleted file mode 100644 index a51a7683..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.actor - -import dev.usbharu.hideout.activitypub.domain.model.Person -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RestController - -@RestController -interface UserAPController { - @GetMapping("/users/{username}") - suspend fun userAp(@PathVariable("username") username: String): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt deleted file mode 100644 index 73b9b8a5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.actor - -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController - -@RestController -class UserAPControllerImpl(private val apUserService: APUserService) : UserAPController { - override suspend fun userAp(username: String): ResponseEntity { - val person = try { - apUserService.getPersonByName(username) - } catch (_: UserNotFoundException) { - return ResponseEntity.notFound().build() - } - person.context += Constant.context - return ResponseEntity(person, HttpStatus.OK) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt deleted file mode 100644 index f89b9260..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.hostmeta - -import dev.usbharu.hideout.application.config.ApplicationConfig -import org.intellij.lang.annotations.Language -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RestController - -@RestController -class HostMetaController(private val applicationConfig: ApplicationConfig) { - - val xml = //language=XML - """ - - -""" - - @Language("JSON") - val json = """{ - "links": [ - { - "rel": "lrdd", - "type": "application/jrd+json", - "template": "${applicationConfig.url}/.well-known/webfinger?resource={uri}" - } - ] -}""" - - @GetMapping("/.well-known/host-meta", produces = ["application/xml"]) - fun hostmeta(): ResponseEntity = ResponseEntity(xml, HttpStatus.OK) - - @GetMapping("/.well-known/host-meta.json", produces = ["application/json"]) - fun hostmetJson(): ResponseEntity = ResponseEntity(json, HttpStatus.OK) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt deleted file mode 100644 index 2cbbdbec..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.inbox - -import jakarta.servlet.http.HttpServletRequest -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RestController - -@RestController -interface InboxController { - @RequestMapping( - "/inbox", - "/users/{username}/inbox", - produces = [ - "application/activity+json", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ], - consumes = ["application/json", "application/*+json"], - method = [RequestMethod.POST] - ) - suspend fun inbox(httpServletRequest: HttpServletRequest): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt deleted file mode 100644 index 5045007b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.inbox - -import dev.usbharu.hideout.activitypub.service.common.APService -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import jakarta.servlet.http.HttpServletRequest -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.slf4j.MDCContext -import kotlinx.coroutines.withContext -import org.slf4j.LoggerFactory -import org.springframework.http.HttpHeaders.WWW_AUTHENTICATE -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController -import java.net.URL - -@RestController -class InboxControllerImpl( - private val apService: APService, - private val httpSignatureHeaderChecker: HttpSignatureHeaderChecker, -) : InboxController { - @Suppress("TooGenericExceptionCaught") - override suspend fun inbox( - httpServletRequest: HttpServletRequest, - ): ResponseEntity { - val headersList = httpServletRequest.headerNames?.toList().orEmpty() - LOGGER.trace("Inbox Headers {}", headersList) - - val body = withContext(Dispatchers.IO + MDCContext()) { - httpServletRequest.inputStream.readAllBytes()!! - } - - val responseEntity = checkHeader(httpServletRequest, body) - - if (responseEntity != null) { - return responseEntity - } - - val parseActivity = try { - apService.parseActivity(body.decodeToString()) - } catch (e: Exception) { - LOGGER.warn("FAILED Parse Activity", e) - return ResponseEntity.accepted().build() - } - LOGGER.info("INBOX Processing Activity Type: {}", parseActivity) - try { - val url = httpServletRequest.requestURL.toString() - - val headers = - headersList.associateWith { header -> - httpServletRequest.getHeaders(header)?.toList().orEmpty() - } - - apService.processActivity( - body.decodeToString(), - parseActivity, - HttpRequest( - URL(url + httpServletRequest.queryString.orEmpty()), - HttpHeaders(headers), - HttpMethod.POST - ), - headers - ) - } catch (e: Exception) { - LOGGER.warn("FAILED Process Activity $parseActivity", e) - return ResponseEntity(HttpStatus.ACCEPTED) - } - LOGGER.info("SUCCESS Processing Activity Type: {}", parseActivity) - return ResponseEntity(HttpStatus.ACCEPTED) - } - - private fun checkHeader( - httpServletRequest: HttpServletRequest, - body: ByteArray, - ): ResponseEntity? { - try { - httpSignatureHeaderChecker.checkDate(httpServletRequest.getHeader("date")!!) - } catch (_: NullPointerException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Required date header") - } catch (_: IllegalArgumentException) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Request is too old.") - } - try { - httpSignatureHeaderChecker.checkHost(httpServletRequest.getHeader("host")!!) - } catch (_: NullPointerException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Required host header") - } catch (_: IllegalArgumentException) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Wrong host for request") - } - try { - httpSignatureHeaderChecker.checkDigest(body, httpServletRequest.getHeader("digest")!!) - } catch (_: NullPointerException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body("Required request body digest in digest header (sha256)") - } catch (_: IllegalArgumentException) { - return ResponseEntity - .status(HttpStatus.UNAUTHORIZED) - .body("Wrong digest for request") - } - - if (httpServletRequest.getHeader("signature").orEmpty().isBlank()) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .header( - WWW_AUTHENTICATE, - "Signature realm=\"Example\",headers=\"(request-target) date host digest\"" - ) - .build() - } - return null - } - - companion object { - private val LOGGER = LoggerFactory.getLogger(InboxControllerImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt deleted file mode 100644 index 00cda72e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.nodeinfo - -import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo -import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0 -import dev.usbharu.hideout.application.config.ApplicationConfig -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RestController - -@RestController -class NodeinfoController(private val applicationConfig: ApplicationConfig) { - @GetMapping("/.well-known/nodeinfo") - fun nodeinfo(): ResponseEntity { - return ResponseEntity( - Nodeinfo( - listOf( - Nodeinfo.Links( - "http://nodeinfo.diaspora.software/ns/schema/2.0", - "${applicationConfig.url}/nodeinfo/2.0" - ) - ) - ), - HttpStatus.OK - ) - } - - @GetMapping("/nodeinfo/2.0") - @Suppress("FunctionNaming") - fun nodeinfo2_0(): ResponseEntity { - return ResponseEntity( - Nodeinfo2_0( - version = "2.0", - software = Nodeinfo2_0.Software( - name = "hideout", - version = "0.0.1" - ), - protocols = listOf("activitypub"), - services = Nodeinfo2_0.Services( - inbound = emptyList(), - outbound = emptyList() - ), - openRegistrations = false, - usage = Nodeinfo2_0.Usage( - users = Nodeinfo2_0.Usage.Users( - total = 1, - activeHalfYear = 1, - activeMonth = 1 - ), - localPosts = 1, - localComments = 0 - ), - metadata = Nodeinfo2_0.Metadata( - nodeName = "hideout", - nodeDescription = "hideout test server", - maintainer = Nodeinfo2_0.Metadata.Maintainer("usbharu", "i@usbharu.dev"), - langs = emptyList(), - tosUrl = "", - repositoryUrl = "https://github.com/usbharu/Hideout", - feedbackUrl = "https://github.com/usbharu/Hideout/issues/new/choose", - ) - ), - HttpStatus.OK - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt deleted file mode 100644 index 2150171a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable - -interface NoteApController { - @GetMapping("/users/*/posts/{postId}") - suspend fun postsAp( - @PathVariable("postId") postId: Long - ): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt deleted file mode 100644 index 79e54dcb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.objects.note.NoteApApiService -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser -import org.springframework.http.ResponseEntity -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RestController - -@RestController -class NoteApControllerImpl(private val noteApApiService: NoteApApiService) : NoteApController { - override suspend fun postsAp( - @PathVariable(value = "postId") postId: Long, - ): ResponseEntity { - val context = SecurityContextHolder.getContext() - val userId = - if (context.authentication is PreAuthenticatedAuthenticationToken && - context.authentication.details is HttpSignatureUser - ) { - (context.authentication.details as HttpSignatureUser).id - } else { - null - } - - val note = noteApApiService.getNote(postId, userId) - if (note != null) { - return ResponseEntity.ok(note) - } - return ResponseEntity.notFound().build() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt deleted file mode 100644 index 0574dd34..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.outbox - -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RestController - -@RestController -interface OutboxController { - @RequestMapping("/outbox", "/users/{username}/outbox", method = [RequestMethod.POST, RequestMethod.GET]) - suspend fun outbox(): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt deleted file mode 100644 index b9ebbbc4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.outbox - -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController - -@RestController -class OutboxControllerImpl : OutboxController { - override suspend fun outbox(): ResponseEntity = - ResponseEntity(HttpStatus.NOT_IMPLEMENTED) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt deleted file mode 100644 index a6d65710..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.webfinger - -import dev.usbharu.hideout.activitypub.domain.model.webfinger.WebFinger -import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.util.AcctUtil -import kotlinx.coroutines.runBlocking -import org.slf4j.LoggerFactory -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestParam - -@Controller -class WebFingerController( - private val webFingerApiService: WebFingerApiService, - private val applicationConfig: ApplicationConfig -) { - @GetMapping("/.well-known/webfinger") - fun webfinger(@RequestParam("resource") resource: String): ResponseEntity = runBlocking { - logger.info("WEBFINGER Lookup webfinger resource: {}", resource) - val acct = try { - AcctUtil.parse(resource.replace("acct:", "")) - } catch (e: IllegalArgumentException) { - logger.warn("FAILED Parse acct.", e) - return@runBlocking ResponseEntity.badRequest().build() - } - val user = try { - webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host) - } catch (_: UserNotFoundException) { - return@runBlocking ResponseEntity.notFound().build() - } - val webFinger = WebFinger( - "acct:${user.name}@${user.domain}", - listOf( - WebFinger.Link( - "self", - "application/activity+json", - user.url - ) - ) - ) - logger.info("SUCCESS Lookup webfinger resource: {} acct: {}", resource, acct) - ResponseEntity(webFinger, HttpStatus.OK) - } - - companion object { - private val logger = LoggerFactory.getLogger(WebFingerController::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt deleted file mode 100644 index 77bf6287..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.query - -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.core.domain.model.post.Post -import org.springframework.stereotype.Repository - -@Repository -interface AnnounceQueryService { - suspend fun findById(id: Long): Pair? - suspend fun findByApId(apId: String): Pair? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt deleted file mode 100644 index 8d76227b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.query - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.core.domain.model.post.Post - -interface NoteQueryService { - suspend fun findById(id: Long): Pair? - suspend fun findByApid(apId: String): Pair? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt deleted file mode 100644 index 7df08bb3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import org.springframework.stereotype.Service - -@Service -class ApAcceptProcessor( - transaction: Transaction, - private val relationshipService: RelationshipService, - private val actorRepository: ActorRepository -) : - AbstractActivityPubProcessor(transaction) { - - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.apObject - - if (value.type.contains("Follow").not()) { - logger.warn("FAILED Activity type isn't Follow.") - throw IllegalActivityPubObjectException("Invalid type ${value.type}") - } - - val follow = value as Follow - - val userUrl = follow.apObject - val followerUrl = follow.actor - - val user = actorRepository.findByUrl(userUrl) ?: throw UserNotFoundException.withUrl(userUrl) - val follower = actorRepository.findByUrl(followerUrl) ?: throw UserNotFoundException.withUrl(followerUrl) - - relationshipService.acceptFollowRequest(user.id, follower.id) - logger.debug("SUCCESS Follow from ${user.url} to ${follower.url}.") - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Accept - - override fun type(): Class = Accept::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt deleted file mode 100644 index ea1793e6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.external.job.DeliverAcceptTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service - -interface ApSendAcceptService { - suspend fun sendAcceptFollow(actor: Actor, target: Actor) -} - -@Service -class ApSendAcceptServiceImpl( - private val owlProducer: OwlProducer, -) : ApSendAcceptService { - override suspend fun sendAcceptFollow(actor: Actor, target: Actor) { - val deliverAcceptTask = DeliverAcceptTask( - Accept( - apObject = Follow( - apObject = actor.url, - actor = target.url - ), - actor = actor.url - ), - target.inbox, - actor.id - ) - - owlProducer.publishTask(deliverAcceptTask) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt deleted file mode 100644 index 17ff0cac..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.announce - -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.application.external.Transaction -import org.springframework.stereotype.Service - -@Service -class ApAnnounceProcessor(transaction: Transaction, private val apNoteService: APNoteService) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - apNoteService.fetchAnnounce(activity.activity) - } - - override fun isSupported(activityType: ActivityType): Boolean = ActivityType.Announce == activityType - - override fun type(): Class = Announce::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt deleted file mode 100644 index 2b8921ca..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.core.domain.model.post.Post - -interface ApSendCreateService { - suspend fun createNote(post: Post) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt deleted file mode 100644 index 0fb4a1e8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.external.job.DeliverCreateTask -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class ApSendCreateServiceImpl( - private val followerQueryService: FollowerQueryService, - private val noteQueryService: NoteQueryService, - private val applicationConfig: ApplicationConfig, - private val actorRepository: ActorRepository, - private val owlProducer: OwlProducer, -) : ApSendCreateService { - override suspend fun createNote(post: Post) { - logger.info("CREATE Create Local Note ${post.url}") - logger.debug("START Create Local Note ${post.url}") - logger.trace("{}", post) - val followers = followerQueryService.findFollowersById(post.actorId) - - logger.debug("DELIVER Deliver Note Create ${followers.size} accounts.") - - val userEntity = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) - val note = noteQueryService.findById(post.id)?.first ?: throw PostNotFoundException.withId(post.id) - val create = Create( - name = "Create Note", - apObject = note, - actor = note.attributedTo, - id = "${applicationConfig.url}/create/note/${post.id}" - ) - followers.forEach { followerEntity -> - owlProducer.publishTask(DeliverCreateTask(create, userEntity.url, followerEntity.inbox)) - } - - logger.debug("SUCCESS Create Local Note ${post.url}") - } - - companion object { - private val logger = LoggerFactory.getLogger(ApSendCreateServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt deleted file mode 100644 index 9be6fa65..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.application.external.Transaction -import org.springframework.stereotype.Service - -@Service -class CreateActivityProcessor(transaction: Transaction, private val apNoteService: APNoteService) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - apNoteService.fetchNote(activity.activity.apObject as Note) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Create - - override fun type(): Class = Create::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt deleted file mode 100644 index a0d789c5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.delete - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.activitypub.domain.model.HasId -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -@Service -class APDeleteProcessor( - transaction: Transaction, - private val userService: UserService, - private val postService: PostService, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.apObject - val deleteId = if (value is HasId) { - value.id - } else if (value is ObjectValue) { - value.`object` - } else { - throw IllegalActivityPubObjectException("object hasn't id or object") - } - - val actor = actorRepository.findByUrl(deleteId) - actor?.let { userService.deleteRemoteActor(it.id) } - - val post = postRepository.findByApId(deleteId) - if (post == null) { - logger.warn("FAILED Delete id: {} is not found.", deleteId) - return - } - postService.deleteRemote(post) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete - - override fun type(): Class = Delete::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt deleted file mode 100644 index 4570808d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.delete - -import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.activitypub.domain.model.Tombstone -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.external.job.DeliverDeleteTask -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service -import java.time.Instant - -interface APSendDeleteService { - suspend fun sendDeleteNote(deletedPost: Post) - suspend fun sendDeleteActor(deletedActor: Actor) -} - -@Service -class APSendDeleteServiceImpl( - private val followerQueryService: FollowerQueryService, - private val applicationConfig: ApplicationConfig, - private val actorRepository: ActorRepository, - private val owlProducer: OwlProducer, -) : APSendDeleteService { - override suspend fun sendDeleteNote(deletedPost: Post) { - val actor = - actorRepository.findById(deletedPost.actorId) ?: throw UserNotFoundException.withId(deletedPost.actorId) - val followersById = followerQueryService.findFollowersById(deletedPost.actorId) - - val delete = Delete( - actor = actor.url, - id = "${applicationConfig.url}/delete/note/${deletedPost.id}", - published = Instant.now().toString(), - `object` = Tombstone(id = deletedPost.apId) - ) - - followersById.forEach { - val jobProps = DeliverDeleteTask( - delete, - it.inbox, - actor.id - ) - - owlProducer.publishTask(jobProps) - } - } - - override suspend fun sendDeleteActor(deletedActor: Actor) { - val followers = followerQueryService.findFollowersById(deletedActor.id) - - val delete = Delete( - actor = deletedActor.url, - `object` = ObjectValue(emptyList(), `object` = deletedActor.url), - id = "${applicationConfig.url}/delete/actor/${deletedActor.id}", - published = Instant.now().toString() - ) - - followers.forEach { - DeliverDeleteTask( - delete = delete, - it.inbox, - deletedActor.id - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt deleted file mode 100644 index 3cc8cc68..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.external.job.ReceiveFollowTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service - -@Service -class APFollowProcessor( - transaction: Transaction, - private val owlProducer: OwlProducer, -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - logger.info("FOLLOW from: {} to {}", activity.activity.actor, activity.activity.apObject) - - // inboxをジョブキューに乗せているので既に不要だが、フォロー承認制アカウントを実装する際に必要なので残す - val jobProps = ReceiveFollowTask( - activity.activity.actor, - activity.activity, - activity.activity.apObject - ) - owlProducer.publishTask(jobProps) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Follow - - override fun type(): Class = Follow::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt deleted file mode 100644 index 9d69e7be..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.core.external.job.ReceiveFollowTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -interface APReceiveFollowService { - suspend fun receiveFollow(follow: Follow) -} - -@Service -class APReceiveFollowServiceImpl( - private val owlProducer: OwlProducer, -) : APReceiveFollowService { - override suspend fun receiveFollow(follow: Follow) { - logger.info("FOLLOW from: {} to: {}", follow.actor, follow.apObject) - owlProducer.publishTask(ReceiveFollowTask(follow.actor, follow, follow.apObject)) - return - } - - companion object { - private val logger = LoggerFactory.getLogger(APReceiveFollowServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt deleted file mode 100644 index 80ac8c7f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.service.follow.SendFollowDto -import org.springframework.stereotype.Service - -interface APSendFollowService { - suspend fun sendFollow(sendFollowDto: SendFollowDto) -} - -@Service -class APSendFollowServiceImpl( - private val apRequestService: APRequestService, - private val applicationConfig: ApplicationConfig, -) : APSendFollowService { - override suspend fun sendFollow(sendFollowDto: SendFollowDto) { - val follow = Follow( - apObject = sendFollowDto.followTargetActorId.url, - actor = sendFollowDto.actorId.url, - id = "${applicationConfig.url}/follow/${sendFollowDto.actorId.id}/${sendFollowDto.followTargetActorId.id}" - ) - - apRequestService.apPost(sendFollowDto.followTargetActorId.inbox, follow, sendFollowDto.actorId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt deleted file mode 100644 index b018b6d4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.service.reaction.ReactionService -import org.springframework.stereotype.Service - -@Service -class APLikeProcessor( - transaction: Transaction, - private val apUserService: APUserService, - private val apNoteService: APNoteService, - private val reactionService: ReactionService, - private val emojiService: EmojiService -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val actor = activity.activity.actor - val content = activity.activity.content - - val target = activity.activity.apObject - - val personWithEntity = apUserService.fetchPersonWithEntity(actor) - - try { - val post = apNoteService.fetchNoteWithEntity(target).second - - val emoji = if (content.startsWith(":")) { - val tag = activity.activity.tag - (tag.firstOrNull { it is Emoji } as? Emoji)?.let { emojiService.fetchEmoji(it).second } - } else { - UnicodeEmoji(content) - } - - reactionService.receiveReaction( - emoji ?: UnicodeEmoji("❤"), - personWithEntity.second.id, - post.id - ) - - logger.debug("SUCCESS Add Like($content) from ${personWithEntity.second.url} to ${post.url}") - } catch (e: FailedToGetActivityPubResourceException) { - logger.debug("FAILED failed to get {}", target) - logger.trace("", e) - return - } - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Like - - override fun type(): Class = Like::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt deleted file mode 100644 index fac9ccff..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.external.job.DeliverReactionTask -import dev.usbharu.hideout.core.external.job.DeliverUndoTask -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service -import java.time.Instant - -interface APReactionService { - suspend fun reaction(like: Reaction) - suspend fun removeReaction(like: Reaction) -} - -@Service -class APReactionServiceImpl( - private val followerQueryService: FollowerQueryService, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val applicationConfig: ApplicationConfig, - private val owlProducer: OwlProducer, -) : APReactionService { - override suspend fun reaction(like: Reaction) { - val followers = followerQueryService.findFollowersById(like.actorId) - val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) - val post = - postRepository.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId) - followers.forEach { follower -> - owlProducer.publishTask( - DeliverReactionTask( - actor = user.url, - like = Like( - actor = user.url, - id = "${applicationConfig.url}/like/note/${post.id}", - content = "❤", - apObject = post.url - ), - inbox = follower.inbox - ) - ) - } - } - - override suspend fun removeReaction(like: Reaction) { - val followers = followerQueryService.findFollowersById(like.actorId) - val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) - val post = - postRepository.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId) - followers.forEach { follower -> - owlProducer.publishTask( - DeliverUndoTask( - signer = user.id, - inbox = follower.inbox, - undo = Undo( - actor = user.url, - id = "${applicationConfig.url}/undo/like/${post.id}", - apObject = Like( - actor = user.url, - id = "${applicationConfig.url}/like/note/${post.id}", - content = "❤", - apObject = post.url - ), - published = Instant.now().toString(), - ) - ) - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt deleted file mode 100644 index 9a3d5bb0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.reject - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Reject -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import org.springframework.stereotype.Service - -@Service -class ApRejectProcessor( - private val relationshipService: RelationshipService, - transaction: Transaction, - private val actorRepository: ActorRepository -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val activityType = activity.activity.apObject.type.firstOrNull { it == "Follow" } - - if (activityType == null) { - logger.warn("FAILED Process Reject Activity type: {}", activity.activity.apObject.type) - return - } - when (activityType) { - "Follow" -> { - val user = actorRepository.findByUrl(activity.activity.actor) ?: throw UserNotFoundException.withUrl( - activity.activity.actor - ) - - activity.activity.apObject as Follow - - val actor = activity.activity.apObject.actor - - val target = actorRepository.findByUrl(actor) ?: throw UserNotFoundException.withUrl(actor) - - logger.debug("REJECT Follow user {} target {}", user.url, target.url) - - relationshipService.rejectFollowRequest(user.id, target.id) - } - - else -> {} - } - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Reject - - override fun type(): Class = Reject::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt deleted file mode 100644 index e925aef0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.reject - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface ApSendRejectService { - suspend fun sendRejectFollow(actor: Actor, target: Actor) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt deleted file mode 100644 index eef8dc2f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.reject - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Reject -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.external.job.DeliverRejectTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service - -@Service -class ApSendRejectServiceImpl( - private val applicationConfig: ApplicationConfig, - private val owlProducer: OwlProducer, -) : ApSendRejectService { - override suspend fun sendRejectFollow(actor: Actor, target: Actor) { - val deliverRejectTask = DeliverRejectTask( - Reject( - actor.url, - "${applicationConfig.url}/reject/${actor.id}/${target.id}", - Follow(apObject = actor.url, actor = target.url) - ), - target.inbox, - actor.id - ) - - owlProducer.publishTask(deliverRejectTask) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt deleted file mode 100644 index 80d4828b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface APSendUndoService { - suspend fun sendUndoFollow(actor: Actor, target: Actor) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt deleted file mode 100644 index 37326c0d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.external.job.DeliverUndoTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class APSendUndoServiceImpl( - private val applicationConfig: ApplicationConfig, - private val owlProducer: OwlProducer, -) : APSendUndoService { - override suspend fun sendUndoFollow(actor: Actor, target: Actor) { - val deliverUndoTask = DeliverUndoTask( - Undo( - actor = actor.url, - id = "${applicationConfig.url}/undo/follow/${actor.id}/${target.url}", - apObject = Follow( - apObject = actor.url, - actor = target.url - ), - published = Instant.now().toString() - ), - target.inbox, - actor.id - ) - - owlProducer.publishTask(deliverUndoTask) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt deleted file mode 100644 index e788929e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.activitypub.domain.model.* -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.local.LocalUserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.core.service.reaction.ReactionService -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -@Service -class APUndoProcessor( - transaction: Transaction, - private val apUserService: APUserService, - private val relationshipService: RelationshipService, - private val reactionService: ReactionService, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val postService: PostService, - private val userService: UserService, -) : AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val undo = activity.activity - - val type = undo.apObject.type.firstOrNull { - it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" - } ?: return - - when (type) { - "Follow" -> { - follow(undo) - return - } - - "Accept" -> { - accept(undo) - return - } - - "Like" -> { - like(undo) - return - } - - "Announce" -> { - announce(undo) - return - } - - "Delete" -> { - delete(undo) - return - } - - else -> {} - } - TODO() - } - - private suspend fun accept(undo: Undo) { - val accept = undo.apObject as Accept - - val acceptObject = if (accept.apObject is ObjectValue) { - accept.apObject.`object` - } else if (accept.apObject is Follow) { - accept.apObject.apObject - } else { - logger.warn("FAILED Unsupported type. Undo Accept {}", accept.apObject.type) - return - } - - val accepter = apUserService.fetchPersonWithEntity(undo.actor, acceptObject).second - val target = actorRepository.findByUrl(acceptObject) ?: throw UserNotFoundException.withUrl(acceptObject) - - relationshipService.rejectFollowRequest(accepter.id, target.id) - return - } - - private suspend fun like(undo: Undo) { - val like = undo.apObject as Like - - val post = postRepository.findByUrl(like.apObject) ?: throw PostNotFoundException.withUrl(like.apObject) - - val signer = actorRepository.findById(post.actorId) ?: throw LocalUserNotFoundException.withId(post.actorId) - val actor = apUserService.fetchPersonWithEntity(like.actor, signer.url).second - - reactionService.receiveRemoveReaction(actor.id, post.id) - return - } - - private suspend fun follow(undo: Undo) { - val follow = undo.apObject as Follow - - val follower = apUserService.fetchPersonWithEntity(undo.actor, follow.apObject).second - val target = actorRepository.findByUrl(follow.apObject) ?: throw UserNotFoundException.withUrl(follow.apObject) - - relationshipService.unfollow(follower.id, target.id) - return - } - - private suspend fun announce(undo: Undo) { - val announce = undo.apObject as Announce - - val findByApId = postRepository.findByApId(announce.id) ?: return - postService.deleteRemote(findByApId) - } - - private suspend fun delete(undo: Undo) { - val announce = undo.apObject as Delete - - val actor = actorRepository.findByUrl(announce.actor) ?: throw UserNotFoundException.withUrl(announce.actor) - - userService.restorationRemoteActor(actor.id) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Undo - - override fun type(): Class = Undo::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt deleted file mode 100644 index 2507e3cb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface APRequestService { - suspend fun apGet(url: String, signer: Actor? = null, responseClass: Class): R - suspend fun apPost( - url: String, - body: T? = null, - signer: Actor? = null, - responseClass: Class - ): R - - suspend fun apPost(url: String, body: T? = null, signer: Actor? = null): String -} - -suspend inline fun APRequestService.apGet(url: String, signer: Actor? = null): R = - apGet(url, signer, R::class.java) - -suspend inline fun APRequestService.apPost( - url: String, - body: T? = null, - signer: Actor? = null -): R = apPost(url, body, signer, R::class.java) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt deleted file mode 100644 index 4b463532..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.util.Base64Util -import dev.usbharu.hideout.util.HttpUtil.Activity -import dev.usbharu.hideout.util.RsaUtil -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.common.PrivateKey -import dev.usbharu.httpsignature.sign.HttpSignatureSigner -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.util.* -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.net.URL -import java.security.MessageDigest -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter - -@Service -class APRequestServiceImpl( - private val httpClient: HttpClient, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val httpSignatureSigner: HttpSignatureSigner, - @Qualifier("http") private val dateTimeFormatter: DateTimeFormatter, -) : APRequestService { - - override suspend fun apGet(url: String, signer: Actor?, responseClass: Class): R { - logger.debug("START ActivityPub Request GET url: {}, signer: {}", url, signer?.url) - val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT"))) - val u = URL(url) - val httpResponse = if (signer?.privateKey == null) { - apGetNotSign(url, date) - } else { - apGetSign(date, u, signer, url) - } - - val bodyAsText = httpResponse.bodyAsText() - val readValue = objectMapper.readValue(bodyAsText, responseClass) - logger.debug( - "SUCCESS ActivityPub Request GET status: {} url: {}", - httpResponse.status, - httpResponse.request.url - ) - logBody(bodyAsText, url) - return readValue - } - - private suspend fun apGetSign( - date: String, - u: URL, - signer: Actor, - url: String, - ): HttpResponse { - val headers = headers { - append("Accept", Activity) - append("Date", date) - append("Host", u.host) - } - - val sign = httpSignatureSigner.sign( - httpRequest = HttpRequest( - url = u, - headers = HttpHeaders(headers.toMap()), - HttpMethod.GET - ), - privateKey = PrivateKey( - keyId = "${signer.url}#pubkey", - privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!), - ), - signHeaders = listOf("(request-target)", "date", "host", "accept") - ) - - val httpResponse = httpClient.get(url) { - headers { - headers { - appendAll(headers) - append("Signature", sign.signatureHeader) -// remove("Host") - } - } - contentType(Activity) - } - return httpResponse - } - - private suspend fun apGetNotSign(url: String, date: String?) = httpClient.get(url) { - header("Accept", Activity) - header("Date", date) - } - - override suspend fun apPost( - url: String, - body: T?, - signer: Actor?, - responseClass: Class, - ): R { - val bodyAsText = apPost(url, body, signer) - return objectMapper.readValue(bodyAsText, responseClass) - } - - override suspend fun apPost(url: String, body: T?, signer: Actor?): String { - logger.debug("START ActivityPub Request POST url: {}, signer: {}", url, signer?.url) - val requestBody = addContextIfNotNull(body) - - logger.trace( - """ - | - |***** BEGIN HTTP Request Trace url: {} ***** - | - |$requestBody - | - |***** END HTTP Request Trace url: {} ***** - | - """.trimMargin(), - url, - url - ) - - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(requestBody.orEmpty().toByteArray())) - - val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT"))) - val u = URL(url) - val httpResponse = if (signer?.privateKey == null) { - apPostNotSign(url, date, digest, requestBody) - } else { - apPostSign(date, u, digest, signer, requestBody) - } - - val bodyAsText = httpResponse.bodyAsText() - logger.debug( - "SUCCESS ActivityPub Request POST status: {} url: {}", - httpResponse.status, - httpResponse.request.url - ) - logBody(bodyAsText, url) - return bodyAsText - } - - private suspend fun apPostNotSign( - url: String, - date: String?, - digest: String, - requestBody: String?, - ) = httpClient.post(url) { - accept(Activity) - header("Date", date) - header("Digest", "sha-256=$digest") - if (requestBody != null) { - setBody(requestBody) - contentType(Activity) - } - } - - private suspend fun apPostSign( - date: String, - u: URL, - digest: String, - signer: Actor, - requestBody: String?, - ): HttpResponse { - val headers = headers { - append("Accept", Activity) - append("Date", date) - append("Host", u.host) - append("Digest", "SHA-256=$digest") - } - - val sign = httpSignatureSigner.sign( - httpRequest = HttpRequest( - u, - HttpHeaders(headers.toMap()), - HttpMethod.POST - ), - privateKey = PrivateKey( - keyId = signer.keyId, - privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!) - ), - signHeaders = listOf("(request-target)", "date", "host", "digest") - ) - - val httpResponse = httpClient.post(u) { - headers { - appendAll(headers) - append("Signature", sign.signatureHeader) -// remove("Host") - } - setBody(requestBody) - contentType(Activity) - } - return httpResponse - } - - private fun addContextIfNotNull(body: T?) = if (body != null) { - val context = mutableListOf() - context.addAll(Constant.context) - context.addAll(body.context) - body.context = context - objectMapper.writeValueAsString(body) - } else { - null - } - - private fun logBody(bodyAsText: String, url: String) { - logger.trace( - """ - | - |***** BEGIN HTTP Response Trace url: {} ***** - | - |$bodyAsText - | - |***** END HTTP Response TRACE url: {} ***** - | - """.trimMargin(), - url, - url - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(APRequestServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt deleted file mode 100644 index bd42e197..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface APResourceResolveService { - suspend fun resolve(url: String, clazz: Class, singer: Actor?): T - suspend fun resolve(url: String, clazz: Class, singerId: Long?): T -} - -suspend inline fun APResourceResolveService.resolve(url: String, singer: Actor?): T = - resolve(url, T::class.java, singer) - -suspend inline fun APResourceResolveService.resolve(url: String, singerId: Long?): T = - resolve(url, T::class.java, singerId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt deleted file mode 100644 index f40589b1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.resource.CacheManager -import dev.usbharu.hideout.core.service.resource.ResolveResponse -import org.springframework.stereotype.Service -import java.io.InputStream - -@Service -class APResourceResolveServiceImpl( - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, - private val cacheManager: CacheManager -) : - APResourceResolveService { - - override suspend fun resolve(url: String, clazz: Class, singerId: Long?): T = - internalResolve(url, singerId, clazz) - - override suspend fun resolve(url: String, clazz: Class, singer: Actor?): T = - internalResolve(url, singer, clazz) - - private suspend fun internalResolve(url: String, singerId: Long?, clazz: Class): T { - val key = genCacheKey(url, singerId) - - cacheManager.putCache(key) { - runResolve(url, singerId?.let { actorRepository.findById(it) }, clazz) - } - return (cacheManager.getOrWait(key) as APResolveResponse).objects - } - - private suspend fun internalResolve(url: String, singer: Actor?, clazz: Class): T { - val key = genCacheKey(url, singer?.id) - cacheManager.putCache(key) { - runResolve(url, singer, clazz) - } - return (cacheManager.getOrWait(key) as APResolveResponse).objects - } - - private suspend fun runResolve(url: String, singer: Actor?, clazz: Class): ResolveResponse = - APResolveResponse(apRequestService.apGet(url, singer, clazz)) - - private fun genCacheKey(url: String, singerId: Long?): String { - if (singerId != null) { - return "$url-$singerId" - } - return url - } - - private class APResolveResponse(val objects: T) : ResolveResponse { - override suspend fun body(): InputStream { - TODO("Not yet implemented") - } - - override suspend fun bodyAsText(): String { - TODO("Not yet implemented") - } - - override suspend fun bodyAsBytes(): ByteArray { - TODO("Not yet implemented") - } - - override suspend fun header(): Map> { - TODO("Not yet implemented") - } - - override suspend fun status(): Int { - TODO("Not yet implemented") - } - - override suspend fun statusMessage(): String { - TODO("Not yet implemented") - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as APResolveResponse<*> - - return objects == other.objects - } - - override fun hashCode(): Int = objects.hashCode() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt deleted file mode 100644 index 64aa581b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import dev.usbharu.hideout.core.external.job.InboxTask -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service - -interface APService { - fun parseActivity(json: String): ActivityType - - suspend fun processActivity( - json: String, - type: ActivityType, - httpRequest: HttpRequest, - map: Map> - ) -} - -@Service -class APServiceImpl( - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val owlProducer: OwlProducer, -) : APService { - - val logger: Logger = LoggerFactory.getLogger(APServiceImpl::class.java) - override fun parseActivity(json: String): ActivityType { - val readTree = try { - objectMapper.readTree(json) - } catch (e: com.fasterxml.jackson.core.JsonParseException) { - throw JsonParseException("Failed to parse json", e) - } - logger.trace( - """ - | - |***** Trace Begin Activity ***** - | - |{} - | - |***** Trace End Activity ***** - | - """.trimMargin(), - readTree.toPrettyString() - ) - if (readTree.isObject.not()) { - throw JsonParseException("Json is not object.") - } - val type = readTree["type"] ?: throw JsonParseException("Type is null") - if (type.isArray) { - try { - return type.firstNotNullOf { jsonNode: JsonNode -> - ActivityType.entries.firstOrNull { it.name.equals(jsonNode.asText(), true) } - } - } catch (e: NoSuchElementException) { - throw IllegalArgumentException("No valid TYPE", e) - } - } - try { - return ActivityType.entries.first { it.name.equals(type.asText(), true) } - } catch (e: NoSuchElementException) { - throw IllegalArgumentException("No valid TYPE", e) - } - } - - override suspend fun processActivity( - json: String, - type: ActivityType, - httpRequest: HttpRequest, - map: Map> - ) { - logger.debug("process activity: {}", type) - owlProducer.publishTask( - InboxTask( - json, - type, - httpRequest, - map - ) - ) - return - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt deleted file mode 100644 index f38d7cc1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.exception.ActivityPubProcessException -import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException -import dev.usbharu.hideout.activitypub.domain.exception.HttpSignatureUnauthorizedException -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.sql.SQLException - -abstract class AbstractActivityPubProcessor( - private val transaction: Transaction, - private val allowUnauthorized: Boolean = false -) : ActivityPubProcessor { - protected val logger: Logger = LoggerFactory.getLogger(this::class.java) - - override suspend fun process(activity: ActivityPubProcessContext) { - if (activity.isAuthorized.not() && allowUnauthorized.not()) { - throw HttpSignatureUnauthorizedException() - } - logger.info("START ActivityPub process. {}", this.type()) - try { - transaction.transaction { - try { - internalProcess(activity) - } catch (e: ResourceAccessException) { - throw SQLException(e) - } - } - } catch (e: ActivityPubProcessException) { - logger.warn("FAILED ActivityPub process", e) - throw FailedProcessException("Failed process", e) - } - logger.info("SUCCESS ActivityPub process. {}", this.type()) - } - - abstract suspend fun internalProcess(activity: ActivityPubProcessContext) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt deleted file mode 100644 index 50d07478..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.JsonNode -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.verify.Signature - -data class ActivityPubProcessContext( - val activity: T, - val jsonNode: JsonNode, - val httpRequest: HttpRequest, - val signature: Signature?, - val isAuthorized: Boolean -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt deleted file mode 100644 index d558e798..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -interface ActivityPubProcessor { - suspend fun process(activity: ActivityPubProcessContext) - - fun isSupported(activityType: ActivityType): Boolean - - fun type(): Class -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt deleted file mode 100644 index acd1fcb6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ActivityType { - Accept, - Add, - Announce, - Arrive, - Block, - Create, - Delete, - Dislike, - Flag, - Follow, - Ignore, - Invite, - Join, - Leave, - Like, - Listen, - Move, - Offer, - Question, - Reject, - Read, - Remove, - TentativeReject, - TentativeAccept, - Travel, - Undo, - Update, - View, - Other -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt deleted file mode 100644 index 4d42dfff..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ActivityVocabulary { - Object, - Link, - Activity, - IntransitiveActivity, - Collection, - OrderedCollection, - CollectionPage, - OrderedCollectionPage, - Accept, - Add, - Announce, - Arrive, - Block, - Create, - Delete, - Dislike, - Flag, - Follow, - Ignore, - Invite, - Join, - Leave, - Like, - Listen, - Move, - Offer, - Question, - Reject, - Read, - Remove, - TentativeReject, - TentativeAccept, - Travel, - Undo, - Update, - View, - Application, - Group, - Organization, - Person, - Service, - Article, - Audio, - Document, - Event, - Image, - Note, - Page, - Place, - Profile, - Relationship, - Tombstone, - Video, - Mention, -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt deleted file mode 100644 index b8150064..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ExtendedActivityVocabulary { - Object, - Link, - Activity, - IntransitiveActivity, - Collection, - OrderedCollection, - CollectionPage, - OrderedCollectionPage, - Accept, - Add, - Announce, - Arrive, - Block, - Create, - Delete, - Dislike, - Flag, - Follow, - Ignore, - Invite, - Join, - Leave, - Like, - Listen, - Move, - Offer, - Question, - Reject, - Read, - Remove, - TentativeReject, - TentativeAccept, - Travel, - Undo, - Update, - View, - Application, - Group, - Organization, - Person, - Service, - Article, - Audio, - Document, - Event, - Image, - Note, - Page, - Place, - Profile, - Relationship, - Tombstone, - Video, - Mention, - Emoji -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt deleted file mode 100644 index ef1c06c6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ExtendedVocabulary { - Emoji -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt deleted file mode 100644 index 7687dcef..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.emoji - -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji - -interface EmojiService { - suspend fun fetchEmoji(url: String): Pair - suspend fun fetchEmoji(emoji: Emoji): Pair - suspend fun findByEmojiName(emojiName: String): CustomEmoji? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt deleted file mode 100644 index 61ba3904..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.emoji - -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveServiceImpl -import dev.usbharu.hideout.activitypub.service.common.resolve -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository -import dev.usbharu.hideout.core.service.instance.InstanceService -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.media.RemoteMedia -import org.springframework.stereotype.Service -import java.net.URL -import java.time.Instant - -@Service -class EmojiServiceImpl( - private val customEmojiRepository: CustomEmojiRepository, - private val instanceService: InstanceService, - private val mediaService: MediaService, - private val apResourceResolveServiceImpl: APResourceResolveServiceImpl, - private val applicationConfig: ApplicationConfig -) : EmojiService { - override suspend fun fetchEmoji(url: String): Pair { - val emoji = apResourceResolveServiceImpl.resolve(url, null as Long?) - return fetchEmoji(emoji) - } - - override suspend fun fetchEmoji(emoji: Emoji): Pair = emoji to save(emoji) - - private suspend fun save(emoji: Emoji): CustomEmoji { - val domain = URL(emoji.id).host - val name = emoji.name.trim(':') - val customEmoji = customEmojiRepository.findByNameAndDomain(name, domain) - - if (customEmoji != null) { - return customEmoji - } - - val instance = instanceService.fetchInstance(emoji.id) - - val media = mediaService.uploadRemoteMedia( - RemoteMedia( - emoji.name, - emoji.icon.url, - emoji.icon.mediaType.orEmpty(), - null - ) - ) - - val customEmoji1 = CustomEmoji( - id = customEmojiRepository.generateId(), - name = name, - domain = domain, - instanceId = instance.id, - url = media.url, - category = null, - createdAt = Instant.now() - ) - - return customEmojiRepository.save(customEmoji1) - } - - override suspend fun findByEmojiName(emojiName: String): CustomEmoji? { - val split = emojiName.trim(':').split("@") - - return when (split.size) { - 1 -> { - customEmojiRepository.findByNameAndDomain(split.first(), applicationConfig.url.host) - } - - 2 -> { - customEmojiRepository.findByNameAndDomain(split.first(), split[1]) - } - - else -> throw IllegalArgumentException("Unknown Emoji Format. $emojiName") - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt deleted file mode 100644 index ea9358a3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.query.AnnounceQueryService -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService -import dev.usbharu.hideout.activitypub.service.common.resolve -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.media.RemoteMedia -import dev.usbharu.hideout.core.service.post.PostService -import io.ktor.client.plugins.* -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -interface APNoteService { - suspend fun fetchNote(url: String, targetActor: String? = null): Note = fetchNoteWithEntity(url, targetActor).first - suspend fun fetchNote(note: Note, targetActor: String? = null): Note - suspend fun fetchNoteWithEntity(url: String, targetActor: String? = null): Pair - - suspend fun fetchAnnounce(url: String, signerId: Long? = null): Pair - suspend fun fetchAnnounce(announce: Announce, signerId: Long? = null): Pair -} - -@Service -@Suppress("LongParameterList") -class APNoteServiceImpl( - private val postRepository: PostRepository, - private val apUserService: APUserService, - private val postService: PostService, - private val apResourceResolveService: APResourceResolveService, - private val postBuilder: Post.PostBuilder, - private val noteQueryService: NoteQueryService, - private val mediaService: MediaService, - private val emojiService: EmojiService, - private val announceQueryService: AnnounceQueryService - -) : APNoteService { - - private val logger = LoggerFactory.getLogger(APNoteServiceImpl::class.java) - - override suspend fun fetchNoteWithEntity(url: String, targetActor: String?): Pair { - logger.debug("START Fetch Note url: {}", url) - - val post = noteQueryService.findByApid(url) - - if (post != null) { - logger.debug("SUCCESS Found in local url: {}", url) - return post - } - - logger.info("AP GET url: {}", url) - val note = try { - apResourceResolveService.resolve(url, null as Long?) - } catch (e: ClientRequestException) { - logger.warn( - "FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", - e.response.status, - url - ) - throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) - } - val savedNote = saveIfMissing(note, targetActor) - logger.debug("SUCCESS Fetch Note url: {}", url) - return savedNote - } - - override suspend fun fetchAnnounce(url: String, signerId: Long?): Pair { - logger.debug("START Fetch Announce url: {}", url) - - val post: Pair? = announceQueryService.findByApId(url) - - if (post != null) { - logger.debug("SUCCESS Found in local url: {}", url) - return post - } - - logger.info("AP GET url: {}", url) - - val announce = try { - apResourceResolveService.resolve(url, signerId) - } catch (e: ClientRequestException) { - logger.warn( - "FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", - e.response.status, - url - ) - throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) - } - - return fetchAnnounce(announce, signerId) - } - - override suspend fun fetchAnnounce(announce: Announce, signerId: Long?): Pair { - val findByApId = announceQueryService.findByApId(announce.id) - - if (findByApId != null) { - return findByApId - } - - val (_, actor) = apUserService.fetchPersonWithEntity(announce.actor, null) - - val (_, post) = fetchNoteWithEntity(announce.apObject, null) - - val visibility = if (announce.to.contains(public)) { - Visibility.PUBLIC - } else if (announce.to.contains(actor.followers) && announce.cc.contains(public)) { - Visibility.UNLISTED - } else if (announce.to.contains(actor.followers)) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } - - val createRemote = postService.createRemote( - postBuilder.pureRepostOf( - id = postRepository.generateId(), - actorId = actor.id, - visibility = visibility, - createdAt = Instant.parse(announce.published), - url = announce.id, - repost = post, - apId = announce.id - ) - ) - return announce to createRemote - } - - private suspend fun saveIfMissing( - note: Note, - targetActor: String? - ): Pair = noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor) - - private suspend fun saveNote(note: Note, targetActor: String?): Pair { - val person = apUserService.fetchPersonWithEntity( - note.attributedTo, - targetActor - ) - - val post = postRepository.findByApId(note.id) - if (post != null) { - return note to post - } - - logger.debug("VISIBILITY url: {} to: {} cc: {}", note.id, note.to, note.cc) - val visibility = visibility(note, person) - logger.debug("VISIBILITY is {} url: {}", visibility.name, note.id) - - val reply = note.inReplyTo?.let { - fetchNote(it, targetActor) - postRepository.findByUrl(it) - } - - val quote = (note.misskeyQuote ?: note.quoteUri ?: note.quoteUrl)?.let { - fetchNote(it, targetActor) - postRepository.findByUrl(it) - } - - val emojis = buildEmojis(note) - - val mediaList = note.attachment.map { - mediaService.uploadRemoteMedia( - RemoteMedia( - it.name, - it.url, - it.mediaType, - description = it.name - ) - ) - }.map { it.id } - - val createPost = - post(quote, person, note, visibility, reply, mediaList, emojis) - - val createRemote = postService.createRemote(createPost) - return note to createRemote - } - - private suspend fun post( - quote: Post?, - person: Pair, - note: Note, - visibility: Visibility, - reply: Post?, - mediaList: List, - emojis: List - ) = if (quote != null) { - postBuilder.quoteRepostOf( - id = postRepository.generateId(), - actorId = person.second.id, - content = note.content, - createdAt = Instant.parse(note.published), - visibility = visibility, - url = note.id, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id, - mediaIds = mediaList, - emojiIds = emojis, - repost = quote - ) - } else { - postBuilder.of( - id = postRepository.generateId(), - actorId = person.second.id, - content = note.content, - createdAt = Instant.parse(note.published).toEpochMilli(), - visibility = visibility, - url = note.id, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id, - mediaIds = mediaList, - emojiIds = emojis - ) - } - - private suspend fun buildEmojis(note: Note) = note.tag - .filterIsInstance() - .map { - emojiService.fetchEmoji(it).second - } - .map { - it.id - } - - private fun visibility( - note: Note, - person: Pair - ): Visibility { - val visibility = if (note.to.contains(public)) { - Visibility.PUBLIC - } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { - Visibility.UNLISTED - } else if (note.to.contains(person.second.followers)) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } - return visibility - } - - override suspend fun fetchNote(note: Note, targetActor: String?): Note = - saveIfMissing(note, targetActor).first - - companion object { - const val public: String = "https://www.w3.org/ns/activitystreams#Public" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt deleted file mode 100644 index 97b1355d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.model.Note - -interface NoteApApiService { - suspend fun getNote(postId: Long, userId: Long?): Note? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt deleted file mode 100644 index df9ba955..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.query.FollowerQueryService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class NoteApApiServiceImpl( - private val noteQueryService: NoteQueryService, - private val followerQueryService: FollowerQueryService, - private val transaction: Transaction -) : NoteApApiService { - override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction { - val findById = noteQueryService.findById(postId) - - if (findById == null) { - logger.warn("Note not found. $postId $userId") - return@transaction null - } - - when (findById.second.visibility) { - Visibility.PUBLIC, Visibility.UNLISTED -> { - return@transaction findById.first - } - - Visibility.FOLLOWERS -> { - return@transaction getFollowersNote(userId, findById) - } - - Visibility.DIRECT -> return@transaction null - } - } - - private suspend fun getFollowersNote( - userId: Long?, - findById: Pair - ): Note? { - if (userId == null) { - return null - } - - if (followerQueryService.alreadyFollow(findById.second.actorId, userId)) { - return findById.first - } - return null - } - - companion object { - private val logger = LoggerFactory.getLogger(NoteApApiServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt deleted file mode 100644 index 336a4de5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.user - -import dev.usbharu.hideout.activitypub.domain.model.Image -import dev.usbharu.hideout.activitypub.domain.model.Key -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService -import dev.usbharu.hideout.activitypub.service.common.resolve -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.user.RemoteUserCreateDto -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -interface APUserService { - suspend fun getPersonByName(name: String): Person - - /** - * Fetch person - * - * @param url - * @param targetActor 署名するユーザー - * @return - */ - suspend fun fetchPerson(url: String, targetActor: String? = null, idOverride: Long? = null): Person - - suspend fun fetchPersonWithEntity( - url: String, - targetActor: String? = null, - idOverride: Long? = null, - ): Pair -} - -@Service -class APUserServiceImpl( - private val userService: UserService, - private val transaction: Transaction, - private val applicationConfig: ApplicationConfig, - private val apResourceResolveService: APResourceResolveService, - private val actorRepository: ActorRepository, -) : - APUserService { - - override suspend fun getPersonByName(name: String): Person { - val userEntity = transaction.transaction { - actorRepository.findByNameAndDomain(name, applicationConfig.url.host) - ?: throw UserNotFoundException.withNameAndDomain(name, applicationConfig.url.host) - } - // TODO: JOINで書き直し - val userUrl = "${applicationConfig.url}/users/$name" - return Person( - type = emptyList(), - name = userEntity.name, - id = userUrl, - preferredUsername = name, - summary = userEntity.description, - inbox = "$userUrl/inbox", - outbox = "$userUrl/outbox", - url = userUrl, - icon = Image( - type = emptyList(), - mediaType = "image/jpeg", - url = "$userUrl/icon.jpg" - ), - publicKey = Key( - id = userEntity.keyId, - owner = userUrl, - publicKeyPem = userEntity.publicKey - ), - endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), - followers = userEntity.followers, - following = userEntity.following, - manuallyApprovesFollowers = userEntity.locked - ) - } - - override suspend fun fetchPerson(url: String, targetActor: String?, idOverride: Long?): Person = - fetchPersonWithEntity(url, targetActor, idOverride).first - - override suspend fun fetchPersonWithEntity( - url: String, - targetActor: String?, - idOverride: Long?, - ): Pair { - val userEntity = actorRepository.findByUrl(url) - - if (userEntity != null && idOverride == null) { - return entityToPerson(userEntity, userEntity.url) to userEntity - } - - val person = apResourceResolveService.resolve(url, null as Long?) - - val id = person.id - - val actor = actorRepository.findByUrlWithLock(id) - - if (actor != null && idOverride == null) { - return person to actor - } - - return person to userService.createRemoteUser( - RemoteUserCreateDto( - name = person.preferredUsername, - domain = id.substringAfter("://").substringBefore("/"), - screenName = person.name ?: person.preferredUsername, - description = person.summary.orEmpty(), - inbox = person.inbox, - outbox = person.outbox, - url = id, - publicKey = person.publicKey.publicKeyPem, - keyId = person.publicKey.id, - following = person.following, - followers = person.followers, - sharedInbox = person.endpoints["sharedInbox"], - locked = person.manuallyApprovesFollowers - ), - idOverride - ) - } - - private fun entityToPerson( - actorEntity: Actor, - id: String, - ) = Person( - type = emptyList(), - name = actorEntity.name, - id = id, - preferredUsername = actorEntity.name, - summary = actorEntity.description, - inbox = "$id/inbox", - outbox = "$id/outbox", - url = id, - icon = Image( - type = emptyList(), - mediaType = "image/jpeg", - url = "$id/icon.jpg" - ), - publicKey = Key( - id = actorEntity.keyId, - owner = id, - publicKeyPem = actorEntity.publicKey - ), - endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), - followers = actorEntity.followers, - following = actorEntity.following, - manuallyApprovesFollowers = actorEntity.locked - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt deleted file mode 100644 index 32a09a76..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.webfinger - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import org.springframework.stereotype.Service - -@Service -interface WebFingerApiService { - suspend fun findByNameAndDomain(name: String, domain: String): Actor -} - -@Service -class WebFingerApiServiceImpl( - private val transaction: Transaction, - private val actorRepository: ActorRepository -) : - WebFingerApiService { - override suspend fun findByNameAndDomain(name: String, domain: String): Actor { - return transaction.transaction { - actorRepository.findByNameAndDomain(name, domain) ?: throw UserNotFoundException.withNameAndDomain( - name, - domain - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index e34fc87c..d51ae754 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -26,7 +26,6 @@ import com.nimbusds.jose.proc.SecurityContext import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer import dev.usbharu.hideout.activitypub.domain.model.StringOrObject import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt new file mode 100644 index 00000000..4854475d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.relationship + +import dev.usbharu.hideout.core.domain.model.relationship.Relationship2 +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody + +class RelationshipEventFactory(private val relationship2: Relationship2) { + fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent { + return DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship2)) + } +} + +class RelationshipEventBody(relationship: Relationship2) : DomainEventBody(mapOf("relationship" to relationship)) + +enum class RelationshipEvent(val eventName: String) { + follow("RelationshipFollow"), + unfollow("RelationshipUnfollow"), + block("RelationshipBlock"), + unblock("RelationshipUnblock"), + mute("RelationshipMute"), + unmute("RelationshipUnmute"), + followRequest("RelationshipFollowRequest"), + unfollowRequest("RelationshipUnfollowRequest"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt deleted file mode 100644 index c8861b42..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.actor - -data class Acct(val username: String, val domain: String? = null, val isRemote: Boolean = domain == null) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt deleted file mode 100644 index 7e747110..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.actor - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.CharacterLimit -import jakarta.validation.Validator -import jakarta.validation.constraints.* -import org.hibernate.validator.constraints.URL -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Component -import java.time.Instant -import kotlin.math.max - -@Deprecated("Actor2を使う") -data class Actor private constructor( - @get:NotNull - @get:Positive - val id: Long, - @get:Pattern(regexp = "^[a-zA-Z0-9_-]{1,300}\$") - @get:Size(min = 1) - val name: String, - val domain: String, - val screenName: String, - val description: String, - @get:URL - val inbox: String, - @get:URL - val outbox: String, - @get:URL - val url: String, - @get:NotBlank - val publicKey: String, - val privateKey: String? = null, - @get:PastOrPresent - val createdAt: Instant, - @get:NotBlank - val keyId: String, - val followers: String? = null, - val following: String? = null, - @get:PositiveOrZero - val instance: Long, - val locked: Boolean, - val followersCount: Int = 0, - val followingCount: Int = 0, - val postsCount: Int = 0, - val lastPostDate: Instant? = null, - val emojis: List = emptyList(), -) { - - @Component - class UserBuilder( - private val characterLimit: CharacterLimit, - private val applicationConfig: ApplicationConfig, - private val validator: Validator, - ) { - - private val logger = LoggerFactory.getLogger(UserBuilder::class.java) - - @Suppress("LongParameterList", "FunctionMinLength", "LongMethod") - fun of( - id: Long, - name: String, - domain: String, - screenName: String, - description: String, - inbox: String, - outbox: String, - url: String, - publicKey: String, - privateKey: String? = null, - createdAt: Instant, - keyId: String, - following: String? = null, - followers: String? = null, - instance: Long, - locked: Boolean, - followersCount: Int = 0, - followingCount: Int = 0, - postsCount: Int = 0, - lastPostDate: Instant? = null, - emojis: List = emptyList(), - ): Actor { - if (id == 0L) { - return Actor( - id = id, - name = name, - domain = domain, - screenName = screenName, - description = description, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = privateKey, - createdAt = createdAt, - keyId = keyId, - followers = followers, - following = following, - instance = instance, - locked = locked, - followersCount = followersCount, - followingCount = followingCount, - postsCount = postsCount, - lastPostDate = lastPostDate, - emojis = emojis - ) - } - - // idは0未満ではいけない - require(id >= 0) { "id must be greater than or equal to 0." } - - // nameは空文字以外を含める必要がある - require(name.isNotBlank()) { "name must contain non-blank characters." } - - // nameは指定された長さ以下である必要がある - val limitedName = if (name.length >= characterLimit.account.id) { - logger.warn("name must not exceed ${characterLimit.account.id} characters.") - name.substring(0, characterLimit.account.id) - } else { - name - } - - // domainは空文字以外を含める必要がある - require(domain.isNotBlank()) { "domain must contain non-blank characters." } - - // domainは指定された長さ以下である必要がある - require(domain.length <= characterLimit.general.domain) { - "domain must not exceed ${characterLimit.general.domain} characters." - } - - // screenNameは空文字以外を含める必要がある - require(screenName.isNotBlank()) { "screenName must contain non-blank characters." } - - // screenNameは指定された長さ以下である必要がある - val limitedScreenName = if (screenName.length >= characterLimit.account.name) { - logger.warn("screenName must not exceed ${characterLimit.account.name} characters.") - screenName.substring(0, characterLimit.account.name) - } else { - screenName - } - - // descriptionは指定された長さ以下である必要がある - val limitedDescription = if (description.length >= characterLimit.account.description) { - logger.warn("description must not exceed ${characterLimit.account.description} characters.") - description.substring(0, characterLimit.account.description) - } else { - description - } - - // ローカルユーザーはpasswordとprivateKeyをnullにしてはいけない - if (domain == applicationConfig.url.host) { - requireNotNull(privateKey) { "password and privateKey must not be null for local users." } - } - - // urlは空文字以外を含める必要がある - require(url.isNotBlank()) { "url must contain non-blank characters." } - - // urlは指定された長さ以下である必要がある - require(url.length <= characterLimit.general.url) { - "url must not exceed ${characterLimit.general.url} characters." - } - - // inboxは空文字以外を含める必要がある - require(inbox.isNotBlank()) { "inbox must contain non-blank characters." } - - // inboxは指定された長さ以下である必要がある - require(inbox.length <= characterLimit.general.url) { - "inbox must not exceed ${characterLimit.general.url} characters." - } - - // outboxは空文字以外を含める必要がある - require(outbox.isNotBlank()) { "outbox must contain non-blank characters." } - - // outboxは指定された長さ以下である必要がある - require(outbox.length <= characterLimit.general.url) { - "outbox must not exceed ${characterLimit.general.url} characters." - } - - require(keyId.isNotBlank()) { - "keyId must contain non-blank characters." - } - - val actor = Actor( - id = id, - name = limitedName, - domain = domain, - screenName = limitedScreenName, - description = limitedDescription, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = privateKey, - createdAt = createdAt, - keyId = keyId, - followers = followers, - following = following, - instance = instance, - locked = locked, - followersCount = max(0, followersCount), - followingCount = max(0, followingCount), - postsCount = max(0, postsCount), - lastPostDate = lastPostDate, - emojis = emojis - ) - - val validate = validator.validate(actor) - - for (constraintViolation in validate) { - throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}") - } - return actor - } - } - - fun incrementFollowing(): Actor = this.copy(followingCount = this.followingCount + 1) - - fun decrementFollowing(): Actor = this.copy(followingCount = this.followingCount - 1) - - fun incrementFollowers(): Actor = this.copy(followersCount = this.followersCount + 1) - - fun decrementFollowers(): Actor = this.copy(followersCount = this.followersCount - 1) - - fun incrementPostsCount(): Actor = this.copy(postsCount = this.postsCount + 1) - - fun decrementPostsCount(): Actor = this.copy(postsCount = this.postsCount - 1) - - fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate) - override fun toString(): String { - return "Actor(" + - "id=$id, " + - "name='$name', " + - "domain='$domain', " + - "screenName='$screenName', " + - "description='$description', " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "publicKey='$publicKey', " + - "privateKey=$privateKey, " + - "createdAt=$createdAt, " + - "keyId='$keyId', " + - "followers=$followers, " + - "following=$following, " + - "instance=$instance, " + - "locked=$locked, " + - "followersCount=$followersCount, " + - "followingCount=$followingCount, " + - "postsCount=$postsCount, " + - "lastPostDate=$lastPostDate, " + - "emojis=$emojis" + - ")" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt deleted file mode 100644 index 0123961c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.actor - -import org.springframework.stereotype.Repository - -@Repository -@Suppress("TooManyFunctions") -interface ActorRepository { - suspend fun save(actor: Actor): Actor - - suspend fun findById(id: Long): Actor? - - suspend fun findByIdWithLock(id: Long): Actor? - - suspend fun findAll(limit: Int, offset: Long): List - - suspend fun findByName(name: String): List - - suspend fun findByNameAndDomain(name: String, domain: String): Actor? - - suspend fun findByNameAndDomainWithLock(name: String, domain: String): Actor? - - suspend fun findByUrl(url: String): Actor? - - suspend fun findByUrlWithLock(url: String): Actor? - - suspend fun findByIds(ids: List): List - - suspend fun findByKeyId(keyId: String): Actor? - - suspend fun delete(id: Long) - - suspend fun nextId(): Long -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt index f5d537c2..4e7e16d6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.emoji @JvmInline -value class EmojiId(private val emojiId: Long) \ No newline at end of file +value class EmojiId(val emojiId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt deleted file mode 100644 index 56e8f33d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.notification - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class ExposedNotificationRepository(private val idGenerateService: IdGenerateService) : NotificationRepository, - AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(notification: Notification): Notification = query { - val singleOrNull = - Notifications.selectAll().where { Notifications.id eq notification.id }.forUpdate().singleOrNull() - if (singleOrNull == null) { - Notifications.insert { - it[id] = notification.id - it[type] = notification.type - it[userId] = notification.userId - it[sourceActorId] = notification.sourceActorId - it[postId] = notification.postId - it[text] = notification.text - it[reactionId] = notification.reactionId - it[createdAt] = notification.createdAt - } - } else { - Notifications.update({ Notifications.id eq notification.id }) { - it[type] = notification.type - it[userId] = notification.userId - it[sourceActorId] = notification.sourceActorId - it[postId] = notification.postId - it[text] = notification.text - it[reactionId] = notification.reactionId - it[createdAt] = notification.createdAt - } - } - notification - } - - override suspend fun findById(id: Long): Notification? = query { - Notifications.selectAll().where { Notifications.id eq id }.singleOrNull()?.toNotifications() - } - - override suspend fun deleteById(id: Long) { - Notifications.deleteWhere { Notifications.id eq id } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedNotificationRepository::class.java) - } -} - -fun ResultRow.toNotifications() = Notification( - id = this[Notifications.id], - type = this[Notifications.type], - userId = this[Notifications.userId], - sourceActorId = this[Notifications.sourceActorId], - postId = this[Notifications.postId], - text = this[Notifications.text], - reactionId = this[Notifications.reactionId], - createdAt = this[Notifications.createdAt], -) - -object Notifications : Table("notifications") { - val id = long("id") - val type = varchar("type", 100) - val userId = long("user_id").references(Actors.id) - val sourceActorId = long("source_actor_id").references(Actors.id).nullable() - val postId = long("post_id").references(Posts.id).nullable() - val text = varchar("text", 3000).nullable() - val reactionId = long("reaction_id").references(Reactions.id).nullable() - val createdAt = timestamp("created_at") - - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt deleted file mode 100644 index 67dc170d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.post - -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.service.post.PostContentFormatter -import jakarta.validation.Validator -import jakarta.validation.constraints.Positive -import org.hibernate.validator.constraints.URL -import org.springframework.stereotype.Component -import java.time.Instant - -@Deprecated("Post2を使う") -data class Post private constructor( - @get:Positive - val id: Long, - @get:Positive - val actorId: Long, - val overview: String? = null, - val content: String, - val text: String, - @get:Positive - val createdAt: Long, - val visibility: Visibility, - @get:URL - val url: String, - val repostId: Long? = null, - val replyId: Long? = null, - val sensitive: Boolean = false, - @get:URL - val apId: String = url, - val mediaIds: List = emptyList(), - val deleted: Boolean = false, - val emojiIds: List = emptyList(), -) { - - @Component - class PostBuilder( - private val characterLimit: CharacterLimit, - private val postContentFormatter: PostContentFormatter, - private val validator: Validator, - ) { - @Suppress("FunctionMinLength", "LongParameterList") - fun of( - id: Long, - actorId: Long, - overview: String? = null, - content: String, - createdAt: Long, - visibility: Visibility, - url: String, - repostId: Long? = null, - replyId: Long? = null, - sensitive: Boolean = false, - apId: String = url, - mediaIds: List = emptyList(), - emojiIds: List = emptyList(), - deleted: Boolean = false, - ): Post { - require(id >= 0) { "id must be greater than or equal to 0." } - - require(actorId >= 0) { "actorId must be greater than or equal to 0." } - - val limitedOverview = if ((overview?.length ?: 0) >= characterLimit.post.overview) { - overview?.substring(0, characterLimit.post.overview) - } else { - overview - } - - val limitedText = if (content.length >= characterLimit.post.text) { - content.substring(0, characterLimit.post.text) - } else { - content - } - - val (html, content1) = postContentFormatter.format(limitedText) - - require(url.isNotBlank()) { "url must contain non-blank characters" } - require(url.length <= characterLimit.general.url) { - "url must not exceed ${characterLimit.general.url} characters." - } - - require((repostId ?: 0) >= 0) { "repostId must be greater then or equal to 0." } - require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." } - - val post = Post( - id = id, - actorId = actorId, - overview = limitedOverview, - content = html, - text = content1, - createdAt = createdAt, - visibility = visibility, - url = url, - repostId = repostId, - replyId = replyId, - sensitive = sensitive, - apId = apId, - mediaIds = mediaIds, - deleted = deleted, - emojiIds = emojiIds - ) - - val validate = validator.validate(post) - - for (constraintViolation in validate) { - throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}") - } - - if (post.deleted) { - return post.delete() - } - - return post - } - - @Suppress("LongParameterList") - fun pureRepostOf( - id: Long, - actorId: Long, - visibility: Visibility, - createdAt: Instant, - url: String, - repost: Post, - apId: String, - ): Post { - // リポストの公開範囲は元のポストより広くてはいけない - val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { - repost.visibility - } else { - visibility - } - - val post = of( - id = id, - actorId = actorId, - overview = null, - content = "", - createdAt = createdAt.toEpochMilli(), - visibility = fixedVisibility, - url = url, - repostId = repost.id, - replyId = null, - sensitive = false, - apId = apId, - mediaIds = emptyList(), - deleted = false, - emojiIds = emptyList() - ) - return post - } - - @Suppress("LongParameterList") - fun quoteRepostOf( - id: Long, - actorId: Long, - overview: String? = null, - content: String, - createdAt: Instant, - visibility: Visibility, - url: String, - repost: Post, - replyId: Long? = null, - sensitive: Boolean = false, - apId: String = url, - mediaIds: List = emptyList(), - emojiIds: List = emptyList(), - ): Post { - // リポストの公開範囲は元のポストより広くてはいけない - val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { - repost.visibility - } else { - visibility - } - - val post = of( - id = id, - actorId = actorId, - overview = overview, - content = content, - createdAt = createdAt.toEpochMilli(), - visibility = fixedVisibility, - url = url, - repostId = repost.id, - replyId = replyId, - sensitive = sensitive, - apId = apId, - mediaIds = mediaIds, - deleted = false, - emojiIds = emojiIds - ) - return post - } - } - - fun isPureRepost(): Boolean = - this.text.isEmpty() && - this.content.isEmpty() && - this.overview == null && - this.replyId == null && - this.repostId != null - - fun delete(): Post = copy(deleted = true) - - fun restore(): Post = copy(deleted = false) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt index f2bffd9a..26bdb9f0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt @@ -23,5 +23,5 @@ interface Post2Repository { suspend fun saveAll(posts: List): List suspend fun findById(id: PostId): Post2? suspend fun findByActorId(id: ActorId): List - suspend fun deleteById(id: PostId) + suspend fun delete(post: Post2) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index 2ed4df88..ee9e4e08 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -22,6 +22,8 @@ class PostContent private constructor(val text: String, val content: String, val companion object { val empty = PostContent("", "", emptyList()) + val contentLength = 5000 + val textLength = 3000 } abstract class PostContentFactory { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt index 995329b1..1c2419bb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt @@ -17,4 +17,8 @@ package dev.usbharu.hideout.core.domain.model.post @JvmInline -value class PostOverview(val overview: String) +value class PostOverview(val overview: String) { + companion object { + val length = 100 + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt deleted file mode 100644 index 561911f9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.post - -import org.springframework.stereotype.Repository - -@Suppress("LongParameterList", "TooManyFunctions") -@Repository -interface PostRepository { - suspend fun generateId(): Long - suspend fun save(post: Post): Post - suspend fun saveAll(posts: List) - suspend fun delete(id: Long) - suspend fun findById(id: Long): Post? - suspend fun findByUrl(url: String): Post? - - suspend fun findByApId(apId: String): Post? - suspend fun existByApIdWithLock(apId: String): Boolean - suspend fun findByActorId(actorId: Long): List - suspend fun findByActorIdAndDeleted(actorId: Long, deleted: Boolean): List - - suspend fun countByActorId(actorId: Long): Int -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt new file mode 100644 index 00000000..5b735964 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.relationship + +import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent +import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventFactory +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable + +class Relationship2( + val actorId: ActorId, + val targetActorId: ActorId, + following: Boolean, + blocking: Boolean, + muting: Boolean, + followRequesting: Boolean, + mutingFollowRequest: Boolean, +) : DomainEventStorable() { + + var following: Boolean = following + private set + var blocking: Boolean = blocking + private set + + var muting: Boolean = muting + private set + var followRequesting: Boolean = followRequesting + private set + var mutingFollowRequest: Boolean = mutingFollowRequest + private set + + + fun follow() { + require(blocking.not()) + following = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.follow)) + } + + fun unfollow() { + following = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unfollow)) + } + + fun block() { + require(following.not()) + blocking = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.block)) + } + + fun unblock() { + blocking = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unblock)) + } + + fun mute() { + muting = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.mute)) + } + + fun unmute() { + muting = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unmute)) + } + + fun muteFollowRequest() { + mutingFollowRequest = true + } + + fun unmuteFollowRequest() { + mutingFollowRequest = false + } + + fun followRequest() { + require(blocking.not()) + followRequesting = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.followRequest)) + } + + fun unfollowRequest() { + followRequesting = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unfollowRequest)) + } + + fun acceptFollowRequest() { + follow() + followRequesting = false + } + + fun rejectFollowRequest() { + followRequesting = false + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt similarity index 73% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt index 14abb53e..c04e6106 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.activitypub.domain.model +package dev.usbharu.hideout.core.domain.model.relationship -interface HasName { - val name: String -} +interface Relationship2Repository { + suspend fun save(relationship2: Relationship2): Relationship2 + suspend fun delete(relationship2: Relationship2) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt deleted file mode 100644 index 4780d30d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.relationship - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList - -/** - * [Relationship]の永続化 - * - */ -interface RelationshipRepository { - /** - * 永続化します - * - * @param relationship 永続化する[Relationship] - * @return 永続化された[Relationship] - */ - suspend fun save(relationship: Relationship): Relationship - - /** - * 永続化されたものを削除します - * - * @param relationship 削除する[Relationship] - */ - suspend fun delete(relationship: Relationship) - - /** - * userIdとtargetUserIdで[Relationship]を取得します - * - * @param actorId 取得するユーザーID - * @param targetActorId 対象ユーザーID - * @return 取得された[Relationship] 存在しない場合nullが返ります - */ - suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? - - suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long) - - suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List - - suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int - - suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int - - @Suppress("FunctionMaxLength") - suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean, - page: Page.PageByMaxId, - ): PaginationList - - suspend fun findByActorIdAndMuting( - actorId: Long, - muting: Boolean, - page: Page.PageByMaxId, - ): PaginationList -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 481c3dd6..5e6b7886 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -20,7 +20,6 @@ import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.dao.id.LongIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt deleted file mode 100644 index a0da8d06..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.shared.domainevent - -abstract class DomainEventStorable { - private val domainEvents: MutableList = mutableListOf() - - protected fun addDomainEvent(domainEvent: DomainEvent) { - domainEvents.add(domainEvent) - } - - fun clearDomainEvents() = domainEvents.clear() - - fun getDomainEvents(): List = domainEvents.toList() -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt deleted file mode 100644 index c9598fb1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsEmojis -import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia -import org.jetbrains.exposed.sql.Query -import org.springframework.stereotype.Component - -@Component -class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : QueryMapper { - override fun map(query: Query): List { - return query.groupBy { it[Posts.id] } - .map { it.value } - .map { - it.first().let(postResultRowMapper::map) - .copy( - mediaIds = it.mapNotNull { resultRow -> resultRow.getOrNull(PostsMedia.mediaId) }, - emojiIds = it.mapNotNull { resultRow -> resultRow.getOrNull(PostsEmojis.emojiId) } - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt deleted file mode 100644 index b18690e3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import org.jetbrains.exposed.sql.ResultRow -import org.springframework.stereotype.Component - -@Component -class PostResultRowMapper(private val postBuilder: Post.PostBuilder) : ResultRowMapper { - override fun map(resultRow: ResultRow): Post { - return postBuilder.of( - id = resultRow[Posts.id], - actorId = resultRow[Posts.actorId], - overview = resultRow[Posts.overview], - content = resultRow[Posts.text], - createdAt = resultRow[Posts.createdAt], - visibility = Visibility.values().first { visibility -> visibility.ordinal == resultRow[Posts.visibility] }, - url = resultRow[Posts.url], - repostId = resultRow[Posts.repostId], - replyId = resultRow[Posts.replyId], - sensitive = resultRow[Posts.sensitive], - apId = resultRow[Posts.apId], - deleted = resultRow[Posts.deleted], - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt deleted file mode 100644 index 2c976d4c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.actor.Actor -import org.jetbrains.exposed.sql.Query -import org.springframework.stereotype.Component - -@Component -class UserQueryMapper(private val actorResultRowMapper: ResultRowMapper) : QueryMapper { - override fun map(query: Query): List = query.map(actorResultRowMapper::map) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt deleted file mode 100644 index 2f6a02d2..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import org.jetbrains.exposed.sql.ResultRow -import org.springframework.stereotype.Component -import java.time.Instant - -@Component -class UserResultRowMapper(private val actorBuilder: Actor.UserBuilder) : ResultRowMapper { - override fun map(resultRow: ResultRow): Actor { - return actorBuilder.of( - id = resultRow[Actors.id], - name = resultRow[Actors.name], - domain = resultRow[Actors.domain], - screenName = resultRow[Actors.screenName], - description = resultRow[Actors.description], - inbox = resultRow[Actors.inbox], - outbox = resultRow[Actors.outbox], - url = resultRow[Actors.url], - publicKey = resultRow[Actors.publicKey], - privateKey = resultRow[Actors.privateKey], - createdAt = Instant.ofEpochMilli((resultRow[Actors.createdAt])), - keyId = resultRow[Actors.keyId], - followers = resultRow[Actors.followers], - following = resultRow[Actors.following], - instance = resultRow[Actors.instance], - locked = resultRow[Actors.locked], - followingCount = resultRow[Actors.followingCount], - followersCount = resultRow[Actors.followersCount], - postsCount = resultRow[Actors.postsCount], - lastPostDate = resultRow[Actors.lastPostAt], - emojis = resultRow[Actors.emojis].split(",").filter { it.isNotEmpty() }.map { it.toLong() } - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt deleted file mode 100644 index 770ad559..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.query.FollowerQueryService -import org.springframework.stereotype.Repository - -@Repository -class FollowerQueryServiceImpl( - private val relationshipRepository: RelationshipRepository, - private val actorRepository: ActorRepository -) : FollowerQueryService { - override suspend fun findFollowersById(id: Long): List { - return actorRepository.findByIds( - relationshipRepository.findByTargetIdAndFollowing(id, true).map { it.actorId } - ) - } - - override suspend fun alreadyFollow(actorId: Long, followerId: Long): Boolean = - relationshipRepository.findByUserIdAndTargetUserId(followerId, actorId)?.following ?: false -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt deleted file mode 100644 index 08bc5de4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class ActorRepositoryImpl( - private val idGenerateService: IdGenerateService, - private val actorResultRowMapper: ResultRowMapper, - private val actorQueryMapper: QueryMapper -) : ActorRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun save(actor: Actor): Actor = query { - val singleOrNull = Actors.selectAll().where { Actors.id eq actor.id }.forUpdate().empty() - if (singleOrNull) { - Actors.insert { - it[id] = actor.id - it[name] = actor.name - it[domain] = actor.domain - it[screenName] = actor.screenName - it[description] = actor.description - it[inbox] = actor.inbox - it[outbox] = actor.outbox - it[url] = actor.url - it[createdAt] = actor.createdAt.toEpochMilli() - it[publicKey] = actor.publicKey - it[privateKey] = actor.privateKey - it[keyId] = actor.keyId - it[following] = actor.following - it[followers] = actor.followers - it[instance] = actor.instance - it[locked] = actor.locked - it[followersCount] = actor.followersCount - it[followingCount] = actor.followingCount - it[postsCount] = actor.postsCount - it[lastPostAt] = actor.lastPostDate - it[emojis] = actor.emojis.joinToString(",") - } - } else { - Actors.update({ Actors.id eq actor.id }) { - it[name] = actor.name - it[domain] = actor.domain - it[screenName] = actor.screenName - it[description] = actor.description - it[inbox] = actor.inbox - it[outbox] = actor.outbox - it[url] = actor.url - it[createdAt] = actor.createdAt.toEpochMilli() - it[publicKey] = actor.publicKey - it[privateKey] = actor.privateKey - it[keyId] = actor.keyId - it[following] = actor.following - it[followers] = actor.followers - it[instance] = actor.instance - it[locked] = actor.locked - it[followersCount] = actor.followersCount - it[followingCount] = actor.followingCount - it[postsCount] = actor.postsCount - it[lastPostAt] = actor.lastPostDate - it[emojis] = actor.emojis.joinToString(",") - } - } - return@query actor - } - - override suspend fun findById(id: Long): Actor? = query { - return@query Actors.selectAll().where { Actors.id eq id }.singleOrNull()?.let(actorResultRowMapper::map) - } - - override suspend fun findByIdWithLock(id: Long): Actor? = query { - return@query Actors.selectAll().where { Actors.id eq id }.forUpdate().singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findAll(limit: Int, offset: Long): List = query { - return@query Actors.selectAll().limit(limit, offset).let(actorQueryMapper::map) - } - - override suspend fun findByName(name: String): List = query { - return@query Actors.selectAll().where { Actors.name eq name }.let(actorQueryMapper::map) - } - - override suspend fun findByNameAndDomain(name: String, domain: String): Actor? = query { - return@query Actors.selectAll().where { Actors.name eq name and (Actors.domain eq domain) }.singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findByNameAndDomainWithLock(name: String, domain: String): Actor? = query { - return@query Actors.selectAll().where { Actors.name eq name and (Actors.domain eq domain) }.forUpdate() - .singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findByUrl(url: String): Actor? = query { - return@query Actors.selectAll().where { Actors.url eq url }.singleOrNull()?.let(actorResultRowMapper::map) - } - - override suspend fun findByUrlWithLock(url: String): Actor? = query { - return@query Actors.selectAll().where { Actors.url eq url }.forUpdate().singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findByIds(ids: List): List = query { - return@query Actors.selectAll().where { Actors.id inList ids }.let(actorQueryMapper::map) - } - - override suspend fun findByKeyId(keyId: String): Actor? = query { - return@query Actors.selectAll().where { Actors.keyId eq keyId }.singleOrNull()?.let(actorResultRowMapper::map) - } - - override suspend fun delete(id: Long): Unit = query { - Actors.deleteWhere { Actors.id.eq(id) } - } - - override suspend fun nextId(): Long = idGenerateService.generateId() - - companion object { - private val logger = LoggerFactory.getLogger(ActorRepositoryImpl::class.java) - } -} - -object Actors : Table("actors") { - val id: Column = long("id") - val name: Column = varchar("name", length = 300) - val domain: Column = varchar("domain", length = 1000) - val screenName: Column = varchar("screen_name", length = 300) - val description: Column = varchar( - "description", - length = 10000 - ) - val inbox: Column = varchar("inbox", length = 1000).uniqueIndex() - val outbox: Column = varchar("outbox", length = 1000).uniqueIndex() - val url: Column = varchar("url", length = 1000).uniqueIndex() - val publicKey: Column = varchar("public_key", length = 10000) - val privateKey: Column = varchar( - "private_key", - length = 10000 - ).nullable() - val createdAt: Column = long("created_at") - val keyId = varchar("key_id", length = 1000) - val following = varchar("following", length = 1000).nullable() - val followers = varchar("followers", length = 1000).nullable() - val instance = long("instance").references(Instance.id) - val locked = bool("locked") - val followingCount = integer("following_count") - val followersCount = integer("followers_count") - val postsCount = integer("posts_count") - val lastPostAt = timestamp("last_post_at").nullable() - val emojis = varchar("emojis", 3000) - override val primaryKey: PrimaryKey = PrimaryKey(id) - - init { - uniqueIndex(name, domain) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt new file mode 100644 index 00000000..9abb2bbe --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.post.* +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher +import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.actorId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.apId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.content +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.createdAt +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.deleted +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.hide +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.id +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.moveTo +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.overview +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.replyId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.repostId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.sensitive +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.text +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.url +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.visibility +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.javatime.timestamp +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class ExposedPost2Repository(override val domainEventPublisher: DomainEventPublisher) : Post2Repository, + AbstractRepository(), DomainEventPublishableRepository { + override suspend fun save(post: Post2): Post2 { + query { + Posts2.upsert { + it[id] = post.id.id + it[actorId] = post.actorId.id + it[overview] = post.overview?.overview + it[content] = post.content.content + it[text] = post.content.text + it[createdAt] = post.createdAt + it[visibility] = post.visibility.name + it[url] = post.url.toString() + it[repostId] = post.repostId?.id + it[replyId] = post.replyId?.id + it[sensitive] = post.sensitive + it[apId] = post.apId.toString() + it[deleted] = post.deleted + it[hide] = post.hide + it[moveTo] = post.moveTo?.id + } + PostsMedia.deleteWhere { + postId eq post.id.id + } + PostsEmojis.deleteWhere { + postId eq post.id.id + } + PostsMedia.batchInsert(post.mediaIds) { + this[PostsMedia.postId] = post.id.id + this[PostsMedia.mediaId] = it.id + } + PostsEmojis.batchInsert(post.emojiIds) { + this[PostsEmojis.postId] = post.id.id + this[PostsEmojis.emojiId] = it.emojiId + } + } + update(post) + return post + } + + override suspend fun saveAll(posts: List): List { + query { + Posts2.batchUpsert(posts, id) { + this[id] = it.id.id + this[actorId] = it.actorId.id + this[overview] = it.overview?.overview + this[content] = it.content.content + this[text] = it.content.text + this[createdAt] = it.createdAt + this[visibility] = it.visibility.name + this[url] = it.url.toString() + this[repostId] = it.repostId?.id + this[replyId] = it.replyId?.id + this[sensitive] = it.sensitive + this[apId] = it.apId.toString() + this[deleted] = it.deleted + this[hide] = it.hide + this[moveTo] = it.moveTo?.id + } + val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id.id to it.id } } + PostsMedia.batchUpsert(mediaIds, PostsMedia.postId) { + this[PostsMedia.postId] = it.first + this[PostsMedia.mediaId] = it.second + } + val emojiIds = posts.flatMap { post -> post.emojiIds.map { post.id.id to it.emojiId } } + PostsEmojis.batchUpsert(emojiIds, PostsEmojis.postId) { + this[PostsEmojis.postId] = it.first + this[PostsEmojis.emojiId] = it.second + } + } + posts.forEach { + update(it) + } + return posts + } + + override suspend fun findById(id: PostId): Post2? { + TODO("Not yet implemented") + } + + override suspend fun findByActorId(id: ActorId): List { + TODO("Not yet implemented") + } + + override suspend fun delete(post: Post2) { + query { + Posts2.deleteWhere { + id eq post.id.id + } + } + update(post) + } + + override val logger: Logger = Companion.logger + + companion object { + private val logger = LoggerFactory.getLogger(ExposedPost2Repository::class.java) + } +} + +object Posts2 : Table("posts") { + val id = long("id") + val actorId = long("actor_id").references(Actors2.id) + val overview = varchar("overview", PostOverview.length).nullable() + val content = varchar("content", PostContent.contentLength) + val text = varchar("text", PostContent.textLength) + val createdAt = timestamp("created_at") + val visibility = varchar("visibility", 100) + val url = varchar("url", 1000) + val repostId = long("repost_id").references(id).nullable() + val replyId = long("reply_id").references(id).nullable() + val sensitive = bool("sensitive") + val apId = varchar("ap_id", 1000) + val deleted = bool("deleted") + val hide = bool("hide") + val moveTo = long("move_to").references(id).nullable() + +} + +object PostsMedia : Table("posts_media") { + val postId = long("post_id").references(id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + val mediaId = long("media_id").references(Media.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + override val primaryKey = PrimaryKey(postId, mediaId) +} + +object PostsEmojis : Table("posts_emojis") { + val postId = long("post_id").references(id) + val emojiId = long("emoji_id").references(CustomEmojis.id) + override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt deleted file mode 100644 index 96180244..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.actorId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.apId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.content -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.createdAt -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.deleted -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.id -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.overview -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.replyId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.repostId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.sensitive -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.text -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.url -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.visibility -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class PostRepositoryImpl( - private val idGenerateService: IdGenerateService, - private val postQueryMapper: QueryMapper, -) : PostRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(post: Post): Post = query { - val singleOrNull = Posts.selectAll().where { id eq post.id }.forUpdate().singleOrNull() - if (singleOrNull == null) { - Posts.insert { - it[id] = post.id - it[actorId] = post.actorId - it[overview] = post.overview - it[content] = post.content - it[text] = post.text - it[createdAt] = post.createdAt - it[visibility] = post.visibility.ordinal - it[url] = post.url - it[repostId] = post.repostId - it[replyId] = post.replyId - it[sensitive] = post.sensitive - it[apId] = post.apId - it[deleted] = post.deleted - } - PostsMedia.batchInsert(post.mediaIds) { - this[PostsMedia.postId] = post.id - this[PostsMedia.mediaId] = it - } - PostsEmojis.batchInsert(post.emojiIds) { - this[PostsEmojis.postId] = post.id - this[PostsEmojis.emojiId] = it - } - } else { - PostsMedia.deleteWhere { - postId eq post.id - } - PostsEmojis.deleteWhere { - postId eq post.id - } - PostsMedia.batchInsert(post.mediaIds) { - this[PostsMedia.postId] = post.id - this[PostsMedia.mediaId] = it - } - PostsEmojis.batchInsert(post.emojiIds) { - this[PostsEmojis.postId] = post.id - this[PostsEmojis.emojiId] = it - } - Posts.update({ id eq post.id }) { - it[actorId] = post.actorId - it[overview] = post.overview - it[content] = post.content - it[text] = post.text - it[createdAt] = post.createdAt - it[visibility] = post.visibility.ordinal - it[url] = post.url - it[repostId] = post.repostId - it[replyId] = post.replyId - it[sensitive] = post.sensitive - it[apId] = post.apId - it[deleted] = post.deleted - } - } - return@query post - } - - override suspend fun saveAll(posts: List) { - Posts.batchUpsert( - posts, - id, - ) { - this[id] = it.id - this[actorId] = it.actorId - this[overview] = it.overview - this[content] = it.content - this[text] = it.text - this[createdAt] = it.createdAt - this[visibility] = it.visibility.ordinal - this[url] = it.url - this[repostId] = it.repostId - this[replyId] = it.replyId - this[sensitive] = it.sensitive - this[apId] = it.apId - this[deleted] = it.deleted - } - val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id to it } } - PostsMedia.batchUpsert( - mediaIds, - PostsMedia.postId - ) { - this[PostsMedia.postId] = it.first - this[PostsMedia.mediaId] = it.second - } - - val emojiIds = posts.flatMap { post -> post.emojiIds.map { post.id to it } } - PostsEmojis.batchUpsert(emojiIds, PostsEmojis.postId) { - this[PostsEmojis.postId] = it.first - this[PostsEmojis.emojiId] = it.second - } - } - - override suspend fun findById(id: Long): Post? = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.id eq id } - .let(postQueryMapper::map) - .singleOrNull() - } - - override suspend fun findByUrl(url: String): Post? = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.url eq url } - .let(postQueryMapper::map) - .singleOrNull() - } - - override suspend fun findByApId(apId: String): Post? = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.apId eq apId } - .let(postQueryMapper::map) - .singleOrNull() - } - - override suspend fun existByApIdWithLock(apId: String): Boolean = query { - return@query Posts.selectAll().where { Posts.apId eq apId }.forUpdate().empty().not() - } - - override suspend fun findByActorId(actorId: Long): List = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.actorId eq actorId }.let(postQueryMapper::map) - } - - override suspend fun findByActorIdAndDeleted(actorId: Long, deleted: Boolean): List { - TODO("Not yet implemented") - } - - override suspend fun countByActorId(actorId: Long): Int = query { - return@query Posts - .selectAll() - .where { Posts.actorId eq actorId } - .count() - .toInt() - } - - override suspend fun delete(id: Long): Unit = query { - Posts.deleteWhere { Posts.id eq id } - } - - companion object { - private val logger = LoggerFactory.getLogger(PostRepositoryImpl::class.java) - } -} - -object Posts : Table() { - val id: Column = long("id") - val actorId: Column = long("actor_id").references(Actors.id) - val overview: Column = varchar("overview", 100).nullable() - val content = varchar("content", 5000) - val text: Column = varchar("text", 3000) - val createdAt: Column = long("created_at") - val visibility: Column = integer("visibility").default(0) - val url: Column = varchar("url", 500) - val repostId: Column = long("repost_id").references(id).nullable() - val replyId: Column = long("reply_id").references(id).nullable() - val sensitive: Column = bool("sensitive").default(false) - val apId: Column = varchar("ap_id", 100).uniqueIndex() - val deleted = bool("deleted").default(false) - override val primaryKey: PrimaryKey = PrimaryKey(id) -} - -object PostsMedia : Table("posts_media") { - val postId = long("post_id").references(id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) - val mediaId = long("media_id").references(Media.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) - override val primaryKey = PrimaryKey(postId, mediaId) -} - -object PostsEmojis : Table("posts_emojis") { - val postId = long("post_id").references(id) - val emojiId = long("emoji_id").references(CustomEmojis.id) - override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index 6ec16ddd..578da4ad 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt index 4a91c24a..6481007b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import kotlinx.coroutines.runBlocking import org.springframework.security.core.userdetails.UserDetails diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 5951928c..bd61adac 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.core.interfaces.api.auth import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CaptchaConfig -import dev.usbharu.hideout.core.service.auth.AuthApiService import dev.usbharu.hideout.core.service.auth.RegisterAccountDto import org.springframework.stereotype.Controller import org.springframework.ui.Model diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt deleted file mode 100644 index 64b2f8c2..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -@Deprecated("Use RelationshipQueryService") -interface FollowerQueryService { - suspend fun findFollowersById(id: Long): List - suspend fun alreadyFollow(actorId: Long, followerId: Long): Boolean -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt deleted file mode 100644 index 130ef8a8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.auth - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface AuthApiService { - suspend fun registerAccount(registerAccountDto: RegisterAccountDto): Actor -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt deleted file mode 100644 index b1a2c6ed..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.auth - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.CaptchaConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.service.user.UserCreateDto -import dev.usbharu.hideout.core.service.user.UserService -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class AuthApiServiceImpl( - private val httpClient: HttpClient, - private val captchaConfig: CaptchaConfig, - private val objectMapper: ObjectMapper, - private val userService: UserService, -) : - AuthApiService { - override suspend fun registerAccount(registerAccountDto: RegisterAccountDto): Actor { - if (captchaConfig.reCaptchaSecretKey != null && captchaConfig.reCaptchaSiteKey != null) { - val get = - httpClient.get( - "https://www.google.com/recaptcha/api/siteverify?secret=" + - captchaConfig.reCaptchaSecretKey + "&response=" + registerAccountDto.recaptchaResponse - ) - val recaptchaResult = objectMapper.readValue(get.bodyAsText()) - logger.debug("reCAPTCHA: {}", recaptchaResult) - require(recaptchaResult.success) - require(!(recaptchaResult.score < 0.5)) - } - - val createLocalUser = userService.createLocalUser( - UserCreateDto( - registerAccountDto.username, - registerAccountDto.username, - "", - registerAccountDto.password - ) - ) - - return createLocalUser - } - - companion object { - private val logger = LoggerFactory.getLogger(AuthApiServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt deleted file mode 100644 index 8bba0722..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.query.model.FilterQueryModel - -interface MuteProcessService { - suspend fun processMute(post: Post, context: List, filters: List): FilterResult? - suspend fun processMutes( - posts: List, - context: List, - filters: List - ): Map -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt deleted file mode 100644 index 491b3985..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterMode.* -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class MuteProcessServiceImpl : MuteProcessService { - override suspend fun processMute( - post: Post, - context: List, - filters: List - ): FilterResult? { - val preprocess = preprocess(context, filters) - - return processMute(post, preprocess) - } - - private suspend fun processMute( - post: Post, - preprocess: List - ): FilterResult? { - logger.trace("process mute post: {}", post) - if (post.overview != null) { - val processMute = processMute(post.overview, preprocess) - - if (processMute != null) { - return processMute - } - } - - val processMute = processMute(post.text, preprocess) - - if (processMute != null) { - return processMute - } - - return null - } - - override suspend fun processMutes( - posts: List, - context: List, - filters: List - ): Map { - val preprocess = preprocess(context, filters) - - return posts.mapNotNull { it to (processMute(it, preprocess) ?: return@mapNotNull null) }.toMap() - } - - private suspend fun processMute(string: String, filters: List): FilterResult? { - for (filter in filters) { - val matchEntire = filter.regex.find(string) - - if (matchEntire != null) { - return FilterResult(filter.filter, matchEntire.value) - } - } - - return null - } - - private fun preprocess(context: List, filters: List): List { - val filterQueryModelList = filters - .filter { it.context.any(context::contains) } - .map { - PreProcessedFilter( - it, - precompileRegex(it) - ) - } - - return filterQueryModelList - } - - private fun precompileRegex(filter: FilterQueryModel): Regex { - logger.trace("precompile regex. filter: {}", filter) - - val regexList = mutableListOf() - - val noneRegexStrings = mutableListOf() - val wholeRegexStrings = mutableListOf() - - for (keyword in filter.keywords) { - when (keyword.mode) { - WHOLE_WORD -> wholeRegexStrings.add(keyword.keyword) - REGEX -> regexList.add(Regex(keyword.keyword)) - NONE -> noneRegexStrings.add(keyword.keyword) - } - } - - val noneRegex = noneRegexStrings.joinToString("|", "(", ")") - val wholeRegex = wholeRegexStrings.joinToString("|", "\\b(", ")\\b") - - val regex = if (noneRegexStrings.isNotEmpty() && wholeRegexStrings.isNotEmpty()) { - Regex("$noneRegex|$wholeRegex") - } else if (noneRegexStrings.isNotEmpty()) { - noneRegex.toRegex() - } else if (wholeRegexStrings.isNotEmpty()) { - wholeRegex.toRegex() - } else { - null - } - - if (regex != null) { - regexList.add(regex) - } - - val pattern = regexList.joinToString(")|(", "(", ")") - logger.trace("precompiled regex {}", pattern) - - return Regex(pattern) - } - - data class PreProcessedFilter(val filter: FilterQueryModel, val regex: Regex) - - companion object { - private val logger = LoggerFactory.getLogger(MuteProcessServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt deleted file mode 100644 index 2e85a805..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.follow - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -data class SendFollowDto(val actorId: Actor, val followTargetActorId: Actor) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt index 37cc9fb4..189ac6fc 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt @@ -17,12 +17,9 @@ package dev.usbharu.hideout.core.service.notification import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.notification.Notification import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.Instant diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt deleted file mode 100644 index 3f03f549..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.notification.Notification -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.reaction.Reaction - -interface NotificationStore { - suspend fun publishNotification( - notification: Notification, - user: Actor, - sourceActor: Actor?, - post: Post?, - reaction: Reaction? - ): Boolean - - suspend fun unpulishNotification(notificationId: Long): Boolean -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt deleted file mode 100644 index 1a177666..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.core.domain.model.post.Post -import org.springframework.stereotype.Service - -@Service -interface PostService { - suspend fun createLocal(post: PostCreateDto): Post - suspend fun createRemote(post: Post): Post - suspend fun deleteLocal(post: Post) - suspend fun deleteRemote(post: Post) - suspend fun deleteByActor(actorId: Long) - suspend fun restoreByRemoteActor(actorId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt deleted file mode 100644 index d1d7a136..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.activitypub.service.activity.create.ApSendCreateService -import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.service.timeline.TimelineService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class PostServiceImpl( - private val postRepository: PostRepository, - private val actorRepository: ActorRepository, - private val timelineService: TimelineService, - private val postBuilder: Post.PostBuilder, - private val apSendCreateService: ApSendCreateService, - private val reactionRepository: ReactionRepository, - private val apSendDeleteService: APSendDeleteService, -) : PostService { - - override suspend fun createLocal(post: PostCreateDto): Post { - logger.info("START Create Local Post user: {}, media: {}", post.userId, post.mediaIds.size) - val create = internalCreate(post, true) - apSendCreateService.createNote(create) - logger.info("SUCCESS Create Local Post url: {}", create.url) - return create - } - - override suspend fun createRemote(post: Post): Post { - logger.info("START Create Remote Post user: {}, remote url: {}", post.actorId, post.apId) - val actor = - actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) - val createdPost = internalCreate(post, false) - logger.info("SUCCESS Create Remote Post url: {}", createdPost.url) - return createdPost - } - - override suspend fun deleteLocal(post: Post) { - if (post.deleted) { - return - } - reactionRepository.deleteByPostId(post.id) - postRepository.save(post.delete()) - val actor = actorRepository.findById(post.actorId) - ?: throw IllegalStateException("actor: ${post.actorId} was not found.") - - apSendDeleteService.sendDeleteNote(post) - - actorRepository.save(actor.decrementPostsCount()) - } - - override suspend fun deleteRemote(post: Post) { - if (post.deleted) { - return - } - reactionRepository.deleteByPostId(post.id) - postRepository.save(post.delete()) - - val actor = actorRepository.findById(post.actorId) - ?: throw IllegalStateException("actor: ${post.actorId} was not found.") - - actorRepository.save(actor.decrementPostsCount()) - } - - override suspend fun deleteByActor(actorId: Long) { - val actor = actorRepository.findById(actorId) - ?: throw IllegalStateException("actor: $actorId was not found.") - - postRepository.findByActorId(actorId).filterNot { it.deleted }.forEach { postRepository.save(it.delete()) } - - actorRepository.save(actor.copy(postsCount = 0, lastPostDate = null)) - } - - override suspend fun restoreByRemoteActor(actorId: Long) { - val actor = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - - val postList = postRepository.findByActorIdAndDeleted(actorId, true).map { it.restore() } - - postRepository.saveAll(postList) - - actorRepository.save(actor.copy(postsCount = actor.postsCount.plus(postList.size))) - } - - private suspend fun internalCreate(post: Post, isLocal: Boolean): Post { - return try { - val save = postRepository.save(post) - timelineService.publishTimeline(post, isLocal) - save - } catch (_: DuplicateException) { - postRepository.findByApId(post.apId) ?: throw PostNotFoundException.withApId(post.apId) - } - } - - private suspend fun internalCreate(post: PostCreateDto, isLocal: Boolean): Post { - val user = actorRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found") - val id = postRepository.generateId() - val createPost = postBuilder.of( - id = id, - actorId = post.userId, - overview = post.overview, - content = post.text, - createdAt = Instant.now().toEpochMilli(), - visibility = post.visibility, - url = "${user.url}/posts/$id", - mediaIds = post.mediaIds, - replyId = post.repolyId, - repostId = post.repostId, - ) - return internalCreate(createPost, isLocal) - } - - companion object { - private val logger = LoggerFactory.getLogger(PostServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt index a2ccd6aa..26fe8d61 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.service.reaction import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException import dev.usbharu.hideout.core.domain.model.emoji.Emoji -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.service.notification.NotificationService diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt deleted file mode 100644 index 513ade8c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.relationship - -interface RelationshipService { - suspend fun followRequest(actorId: Long, targetId: Long) - suspend fun block(actorId: Long, targetId: Long) - - /** - * フォローリクエストを承認します - * [actorId]が[targetId]からのフォローリクエストを承認します - * - * @param actorId 承認操作をするユーザー - * @param targetId 承認するフォローリクエストを送ってきたユーザー - * @param force 強制的にAcceptアクティビティを発行する - */ - suspend fun acceptFollowRequest(actorId: Long, targetId: Long, force: Boolean = false) - suspend fun rejectFollowRequest(actorId: Long, targetId: Long) - suspend fun ignoreFollowRequest(actorId: Long, targetId: Long) - suspend fun unfollow(actorId: Long, targetId: Long) - suspend fun unblock(actorId: Long, targetId: Long) - suspend fun mute(actorId: Long, targetId: Long) - suspend fun unmute(actorId: Long, targetId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt deleted file mode 100644 index 057a5224..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.relationship - -import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService -import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService -import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService -import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.relationship.Relationship -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.follow.SendFollowDto -import dev.usbharu.hideout.core.service.notification.FollowNotificationRequest -import dev.usbharu.hideout.core.service.notification.FollowRequestNotificationRequest -import dev.usbharu.hideout.core.service.notification.NotificationService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class RelationshipServiceImpl( - private val applicationConfig: ApplicationConfig, - private val relationshipRepository: RelationshipRepository, - private val apSendFollowService: APSendFollowService, - private val apSendAcceptService: ApSendAcceptService, - private val apSendRejectService: ApSendRejectService, - private val apSendUndoService: APSendUndoService, - private val actorRepository: ActorRepository, - private val notificationService: NotificationService, -) : RelationshipService { - override suspend fun followRequest(actorId: Long, targetId: Long) { - logger.info("START Follow Request userId: {} targetId: {}", actorId, targetId) - - val relationship = - relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)?.copy(followRequest = true) - ?: Relationship( - actorId = actorId, - targetActorId = targetId, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) ?: Relationship( - actorId = targetId, - targetActorId = actorId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - if (inverseRelationship.blocking) { - logger.debug("FAILED Blocked by target. userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.blocking) { - logger.debug("FAILED Blocking user. userId: {} targetId: {}", actorId, targetId) - return - } - if (relationship.ignoreFollowRequestToTarget) { - logger.debug("SUCCESS Ignore Follow Request. userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.following) { - logger.debug("SUCCESS User already follow. userId: {} targetId: {}", actorId, targetId) - acceptFollowRequest(targetId, actorId, true) - return - } - - relationshipRepository.save(relationship) - - val remoteUser = isRemoteUser(targetId) - - if (remoteUser != null) { - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - apSendFollowService.sendFollow(SendFollowDto(user, remoteUser)) - } else { - val target = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - if (target.locked.not()) { - acceptFollowRequest(targetId, actorId) - } else { - notificationService.publishNotify(FollowRequestNotificationRequest(targetId, actorId)) - } - } - - logger.info("SUCCESS Follow Request userId: {} targetId: {}", actorId, targetId) - } - - override suspend fun block(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - val targetActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - if (relationship?.following == true) { - actorRepository.save(user.decrementFollowing()) - actorRepository.save(targetActor.decrementFollowers()) - } - - val blockedRelationship = relationship - ?.copy(blocking = true, followRequest = false, following = false) ?: Relationship( - actorId = actorId, - targetActorId = targetId, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - - if (inverseRelationship?.following == true) { - actorRepository.save(targetActor.decrementFollowing()) - actorRepository.save(user.decrementFollowers()) - } - - val blockedInverseRelationship = inverseRelationship - ?.copy(followRequest = false, following = false) - - relationshipRepository.save(blockedRelationship) - if (blockedInverseRelationship != null) { - relationshipRepository.save(blockedInverseRelationship) - } - } - - override suspend fun acceptFollowRequest(actorId: Long, targetId: Long, force: Boolean) { - logger.info("START Accept follow request userId: {} targetId: {}", actorId, targetId) - - val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) ?: Relationship( - actorId = targetId, - targetActorId = actorId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - if (relationship == null) { - logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.followRequest.not() && force.not()) { - logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.blocking) { - logger.warn("FAILED Blocking user userId: {} targetId: {}", actorId, targetId) - throw IllegalStateException( - "Cannot accept a follow request from a blocked user. userId: $actorId targetId: $targetId" - ) - } - - if (inverseRelationship.blocking) { - logger.warn("FAILED BLocked by user userId: {} targetId: {}", actorId, targetId) - throw IllegalStateException( - "Cannot accept a follow request from a blocking user. userId: $actorId targetId: $targetId" - ) - } - - val copy = relationship.copy(followRequest = false, following = true, blocking = false) - - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - - actorRepository.save(user.incrementFollowers()) - - relationshipRepository.save(copy) - - val remoteActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - - actorRepository.save(remoteActor.incrementFollowing()) - - if (isRemoteActor(remoteActor)) { - apSendAcceptService.sendAcceptFollow(user, remoteActor) - } - notificationService.publishNotify(FollowNotificationRequest(actorId, targetId)) - } - - override suspend fun rejectFollowRequest(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - - if (relationship == null) { - logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.followRequest.not() && relationship.following.not()) { - logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", actorId, targetId) - return - } - - val copy = relationship.copy(followRequest = false, following = false) - - relationshipRepository.save(copy) - - val remoteUser = isRemoteUser(targetId) - - if (remoteUser != null) { - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - apSendRejectService.sendRejectFollow(user, remoteUser) - } - } - - override suspend fun ignoreFollowRequest(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - ?.copy(ignoreFollowRequestToTarget = true) - ?: Relationship( - actorId = targetId, - targetActorId = actorId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = true - ) - - relationshipRepository.save(relationship) - } - - override suspend fun unfollow(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - - if (relationship == null) { - logger.warn("FAILED Unfollow. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - val targetActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - - if (relationship.following) { - actorRepository.save(user.decrementFollowing()) - actorRepository.save(targetActor.decrementFollowers()) - } - - if (relationship.following.not()) { - logger.warn("SUCCESS User already unfollow. userId: {} targetId: {}", actorId, targetId) - return - } - - val copy = relationship.copy(following = false) - - relationshipRepository.save(copy) - - val remoteUser = isRemoteUser(targetId) - - if (remoteUser != null) { - apSendUndoService.sendUndoFollow(user, remoteUser) - } - } - - override suspend fun unblock(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - - if (relationship == null) { - logger.warn("FAILED Unblock. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.blocking.not()) { - logger.warn("SUCCESS User is not blocking. userId: {] targetId: {}", actorId, targetId) - return - } - - val copy = relationship.copy(blocking = false) - relationshipRepository.save(copy) - } - - override suspend fun mute(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)?.copy(muting = true) - ?: Relationship( - actorId = actorId, - targetActorId = targetId, - following = false, - blocking = false, - muting = true, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - relationshipRepository.save(relationship) - } - - override suspend fun unmute(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)?.copy(muting = false) - - if (relationship == null) { - logger.warn("FAILED Mute. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - relationshipRepository.save(relationship) - } - - private fun isRemoteActor(actor: Actor): Boolean = actor.domain != applicationConfig.url.host - - private suspend fun isRemoteUser(userId: Long): Actor? { - logger.trace("isRemoteUser({})", userId) - val user = - actorRepository.findById(userId) ?: throw UserNotFoundException.withId(userId) - - logger.trace("user info {}", user) - - if (user.domain == applicationConfig.url.host) { - logger.trace("user: {} is local user", userId) - return null - } - logger.trace("user: {} is remote user", userId) - return user - } - - companion object { - private val logger = LoggerFactory.getLogger(RelationshipServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt deleted file mode 100644 index 1bdec5c0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.timeline - -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.domain.model.timeline.Timeline -import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository -import dev.usbharu.hideout.core.query.FollowerQueryService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class TimelineService( - private val followerQueryService: FollowerQueryService, - private val timelineRepository: TimelineRepository, - private val actorRepository: ActorRepository -) { - suspend fun publishTimeline(post: Post, isLocal: Boolean) { - val findFollowersById = followerQueryService.findFollowersById(post.actorId).toMutableList() - if (isLocal) { - // 自分自身も含める必要がある - val user = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) - findFollowersById.add(user) - } - val timelines = findFollowersById.map { - Timeline( - id = timelineRepository.generateId(), - userId = it.id, - timelineId = 0, - postId = post.id, - postActorId = post.actorId, - createdAt = post.createdAt, - replyId = post.replyId, - repostId = post.repostId, - visibility = post.visibility, - sensitive = post.sensitive, - isLocal = isLocal, - isPureRepost = post.repostId == null || (post.text.isBlank() && post.overview.isNullOrBlank()), - mediaIds = post.mediaIds, - emojiIds = post.emojiIds - ) - }.toMutableList() - if (post.visibility == Visibility.PUBLIC) { - timelines.add( - Timeline( - id = timelineRepository.generateId(), - userId = 0, - timelineId = 0, - postId = post.id, - postActorId = post.actorId, - createdAt = post.createdAt, - replyId = post.replyId, - repostId = post.repostId, - visibility = post.visibility, - sensitive = post.sensitive, - isLocal = isLocal, - isPureRepost = post.repostId == null || (post.text.isBlank() && post.overview.isNullOrBlank()), - mediaIds = post.mediaIds, - emojiIds = post.emojiIds - ) - ) - } - timelineRepository.saveAll(timelines) - logger.debug("SUCCESS Timeline published. {}", timelines.size) - } - - companion object { - private val logger = LoggerFactory.getLogger(TimelineService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt index ad372b37..77c90ace 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.stereotype.Service import java.security.* diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt deleted file mode 100644 index f1d42b42..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import org.springframework.stereotype.Service - -@Service -interface UserService { - - suspend fun usernameAlreadyUse(username: String): Boolean - - suspend fun createLocalUser(user: UserCreateDto): Actor - - suspend fun createRemoteUser(user: RemoteUserCreateDto, idOverride: Long? = null): Actor - - suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) - - suspend fun deleteRemoteActor(actorId: Long) - - suspend fun restorationRemoteActor(actorId: Long) - - suspend fun deleteLocalUser(userId: Long) - - suspend fun updateUserStatistics(userId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt deleted file mode 100644 index fbc80538..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import dev.usbharu.hideout.core.external.job.UpdateActorTask -import dev.usbharu.hideout.core.service.instance.InstanceService -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -@Suppress("LongParameterList") -class UserServiceImpl( - private val actorRepository: ActorRepository, - private val userAuthService: UserAuthService, - private val actorBuilder: Actor.UserBuilder, - private val applicationConfig: ApplicationConfig, - private val instanceService: InstanceService, - private val userDetailRepository: UserDetailRepository, - private val deletedActorRepository: DeletedActorRepository, - private val reactionRepository: ReactionRepository, - private val relationshipRepository: RelationshipRepository, - private val postService: PostService, - private val apSendDeleteService: APSendDeleteService, - private val postRepository: PostRepository, - private val owlProducer: OwlProducer, -) : - UserService { - - override suspend fun usernameAlreadyUse(username: String): Boolean { - val findByNameAndDomain = actorRepository.findByNameAndDomain(username, applicationConfig.url.host) - return findByNameAndDomain != null - } - - override suspend fun createLocalUser(user: UserCreateDto): Actor { - if (applicationConfig.private) { - throw IllegalStateException("Instance is a private mode.") - } - - val nextId = actorRepository.nextId() - val hashedPassword = userAuthService.hash(user.password) - val keyPair = userAuthService.generateKeyPair() - val userUrl = "${applicationConfig.url}/users/${user.name}" - val userEntity = actorBuilder.of( - id = nextId, - name = user.name, - domain = applicationConfig.url.host, - screenName = user.screenName, - description = user.description, - inbox = "$userUrl/inbox", - outbox = "$userUrl/outbox", - url = userUrl, - publicKey = keyPair.public.toPem(), - privateKey = keyPair.private.toPem(), - createdAt = Instant.now(), - following = "$userUrl/following", - followers = "$userUrl/followers", - keyId = "$userUrl#pubkey", - locked = false, - instance = 0 - ) - val save = actorRepository.save(userEntity) - userDetailRepository.save(UserDetail(nextId, hashedPassword, true)) - return save - } - - override suspend fun createRemoteUser(user: RemoteUserCreateDto, idOverride: Long?): Actor { - logger.info("START Create New remote user. name: {} url: {}", user.name, user.url) - - val deletedActor = deletedActorRepository.findByNameAndDomain(user.name, user.domain) - - if (deletedActor != null) { - logger.warn("FAILED Deleted actor. user: ${user.name} domain: ${user.domain}") - throw IllegalStateException("Cannot create Deleted actor.") - } - - val instance = instanceService.fetchInstance(user.url, user.sharedInbox) - - val nextId = actorRepository.nextId() - val userEntity = actorBuilder.of( - id = idOverride ?: nextId, - name = user.name, - domain = user.domain, - screenName = user.screenName, - description = user.description, - inbox = user.inbox, - outbox = user.outbox, - url = user.url, - publicKey = user.publicKey, - createdAt = Instant.now(), - followers = user.followers, - following = user.following, - keyId = user.keyId, - instance = instance.id, - locked = user.locked ?: false - ) - return try { - val save = actorRepository.save(userEntity) - logger.warn("SUCCESS Create New remote user. id: {} name: {} url: {}", userEntity.id, user.name, user.url) - save - } catch (_: DuplicateException) { - actorRepository.findByUrl(user.url)!! - } - } - - override suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) { - val userDetail = userDetailRepository.findByActorId(userId) - ?: throw IllegalArgumentException("userId: $userId was not found.") - - val actor = actorRepository.findById(userId) ?: throw IllegalArgumentException("userId $userId was not found.") - - actorRepository.save( - actor.copy( - screenName = updateUserDto.screenName, - description = updateUserDto.description, - locked = updateUserDto.locked - ) - ) - - userDetailRepository.save( - userDetail.copy( - autoAcceptFolloweeFollowRequest = updateUserDto.autoAcceptFolloweeFollowRequest - ) - ) - } - - override suspend fun deleteRemoteActor(actorId: Long) { - val actor = actorRepository.findByIdWithLock(actorId) ?: throw UserNotFoundException.withId(actorId) - val deletedActor = DeletedActor( - id = actor.id, - name = actor.name, - domain = actor.domain, - apId = actor.url, - publicKey = actor.publicKey, - deletedAt = Instant.now() - ) - relationshipRepository.deleteByActorIdOrTargetActorId(actorId, actorId) - - reactionRepository.deleteByActorId(actorId) - - postService.deleteByActor(actorId) - - actorRepository.delete(actor.id) - deletedActorRepository.save(deletedActor) - } - - override suspend fun restorationRemoteActor(actorId: Long) { - val deletedActor = deletedActorRepository.findById(actorId) - ?: return - - deletedActorRepository.delete(deletedActor) - - owlProducer.publishTask(UpdateActorTask(deletedActor.id, deletedActor.apId)) - } - - override suspend fun deleteLocalUser(userId: Long) { - val actor = actorRepository.findByIdWithLock(userId) ?: throw UserNotFoundException.withId(userId) - apSendDeleteService.sendDeleteActor(actor) - val deletedActor = DeletedActor( - id = actor.id, - name = actor.name, - domain = actor.domain, - apId = actor.url, - publicKey = actor.publicKey, - deletedAt = Instant.now() - ) - relationshipRepository.deleteByActorIdOrTargetActorId(userId, userId) - - reactionRepository.deleteByActorId(actor.id) - - postService.deleteByActor(actor.id) - actorRepository.delete(actor.id) - val userDetail = - userDetailRepository.findByActorId(actor.id) ?: throw IllegalStateException("user detail not found.") - userDetailRepository.delete(userDetail) - deletedActorRepository.save(deletedActor) - } - - override suspend fun updateUserStatistics(userId: Long) { - val actor = actorRepository.findByIdWithLock(userId) ?: throw UserNotFoundException.withId(userId) - - val followerCount = relationshipRepository.countByTargetIdAndFollowing(userId, true) - val followingCount = relationshipRepository.countByUserIdAndFollowing(userId, true) - val postsCount = postRepository.countByActorId(userId) - - actorRepository.save( - actor.copy( - followersCount = followerCount, - followingCount = followingCount, - postsCount = postsCount - ) - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt deleted file mode 100644 index c4bce048..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.config - -import dev.usbharu.hideout.application.infrastructure.springframework.RoleHierarchyAuthorizationManagerFactory -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.core.annotation.Order -import org.springframework.http.HttpMethod.* -import org.springframework.security.access.hierarchicalroles.RoleHierarchy -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.invoke -import org.springframework.security.web.SecurityFilterChain - -@Configuration -class MastodonApiSecurityConfig { - @Bean - @Order(4) - @Suppress("LongMethod") - fun mastodonApiSecurityFilterChain( - http: HttpSecurity, - rf: RoleHierarchyAuthorizationManagerFactory, - ): SecurityFilterChain { - http { - securityMatcher("/api/v1/**", "/api/v2/**") - authorizeHttpRequests { - authorize(POST, "/api/v1/apps", permitAll) - authorize(GET, "/api/v1/instance/**", permitAll) - authorize(POST, "/api/v1/accounts", authenticated) - - authorize(GET, "/api/v1/accounts/verify_credentials", rf.hasScope("read:accounts")) - authorize(GET, "/api/v1/accounts/relationships", rf.hasScope("read:follows")) - authorize(GET, "/api/v1/accounts/*", permitAll) - authorize(GET, "/api/v1/accounts/*/statuses", permitAll) - authorize(POST, "/api/v1/accounts/*/follow", rf.hasScope("write:follows")) - authorize(POST, "/api/v1/accounts/*/unfollow", rf.hasScope("write:follows")) - authorize(POST, "/api/v1/accounts/*/block", rf.hasScope("write:blocks")) - authorize(POST, "/api/v1/accounts/*/unblock", rf.hasScope("write:blocks")) - authorize(POST, "/api/v1/accounts/*/mute", rf.hasScope("write:mutes")) - authorize(POST, "/api/v1/accounts/*/unmute", rf.hasScope("write:mutes")) - authorize(GET, "/api/v1/mutes", rf.hasScope("read:mutes")) - - authorize(POST, "/api/v1/media", rf.hasScope("write:media")) - authorize(POST, "/api/v1/statuses", rf.hasScope("write:statuses")) - authorize(GET, "/api/v1/statuses/*", permitAll) - authorize(POST, "/api/v1/statuses/*/favourite", rf.hasScope("write:favourites")) - authorize(POST, "/api/v1/statuses/*/unfavourite", rf.hasScope("write:favourites")) - authorize(PUT, "/api/v1/statuses/*/emoji_reactions/*", rf.hasScope("write:favourites")) - - authorize(GET, "/api/v1/timelines/public", permitAll) - authorize(GET, "/api/v1/timelines/home", rf.hasScope("read:statuses")) - - authorize(GET, "/api/v2/filters", rf.hasScope("read:filters")) - authorize(POST, "/api/v2/filters", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/*", rf.hasScope("read:filters")) - authorize(PUT, "/api/v2/filters/*", rf.hasScope("write:filters")) - authorize(DELETE, "/api/v2/filters/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/*/keywords", rf.hasScope("read:filters")) - authorize(POST, "/api/v2/filters/*/keywords", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/keywords/*", rf.hasScope("read:filters")) - authorize(PUT, "/api/v2/filters/keywords/*", rf.hasScope("write:filters")) - authorize(DELETE, "/api/v2/filters/keywords/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/*/statuses", rf.hasScope("read:filters")) - authorize(POST, "/api/v2/filters/*/statuses", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/statuses/*", rf.hasScope("read:filters")) - authorize(DELETE, "/api/v2/filters/statuses/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v1/filters", rf.hasScope("read:filters")) - authorize(POST, "/api/v1/filters", rf.hasScope("write:filters")) - - authorize(GET, "/api/v1/filters/*", rf.hasScope("read:filters")) - authorize(POST, "/api/v1/filters/*", rf.hasScope("write:filters")) - authorize(DELETE, "/api/v1/filters/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v1/notifications", rf.hasScope("read:notifications")) - authorize(GET, "/api/v1/notifications/*", rf.hasScope("read:notifications")) - authorize(POST, "/api/v1/notifications/clear", rf.hasScope("write:notifications")) - authorize(POST, "/api/v1/notifications/*/dismiss", rf.hasScope("write:notifications")) - - authorize(anyRequest, authenticated) - } - - oauth2ResourceServer { - jwt { } - } - - csrf { - ignoringRequestMatchers("/api/v1/apps") - } - } - - return http.build() - } - - @Bean - fun roleHierarchy(): RoleHierarchy { - val roleHierarchyImpl = RoleHierarchyImpl() - - roleHierarchyImpl.setHierarchy( - """ - SCOPE_read > SCOPE_read:accounts - SCOPE_read > SCOPE_read:accounts - SCOPE_read > SCOPE_read:blocks - SCOPE_read > SCOPE_read:bookmarks - SCOPE_read > SCOPE_read:favourites - SCOPE_read > SCOPE_read:filters - SCOPE_read > SCOPE_read:follows - SCOPE_read > SCOPE_read:lists - SCOPE_read > SCOPE_read:mutes - SCOPE_read > SCOPE_read:notifications - SCOPE_read > SCOPE_read:search - SCOPE_read > SCOPE_read:statuses - SCOPE_write > SCOPE_write:accounts - SCOPE_write > SCOPE_write:blocks - SCOPE_write > SCOPE_write:bookmarks - SCOPE_write > SCOPE_write:conversations - SCOPE_write > SCOPE_write:favourites - SCOPE_write > SCOPE_write:filters - SCOPE_write > SCOPE_write:follows - SCOPE_write > SCOPE_write:lists - SCOPE_write > SCOPE_write:media - SCOPE_write > SCOPE_write:mutes - SCOPE_write > SCOPE_write:notifications - SCOPE_write > SCOPE_write:reports - SCOPE_write > SCOPE_write:statuses - SCOPE_follow > SCOPE_write:blocks - SCOPE_follow > SCOPE_write:follows - SCOPE_follow > SCOPE_write:mutes - SCOPE_follow > SCOPE_read:blocks - SCOPE_follow > SCOPE_read:follows - SCOPE_follow > SCOPE_read:mutes - SCOPE_admin > SCOPE_admin:read - SCOPE_admin > SCOPE_admin:write - SCOPE_admin:read > SCOPE_admin:read:accounts - SCOPE_admin:read > SCOPE_admin:read:reports - SCOPE_admin:read > SCOPE_admin:read:domain_allows - SCOPE_admin:read > SCOPE_admin:read:domain_blocks - SCOPE_admin:read > SCOPE_admin:read:ip_blocks - SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks - SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks - SCOPE_admin:write > SCOPE_admin:write:accounts - SCOPE_admin:write > SCOPE_admin:write:reports - SCOPE_admin:write > SCOPE_admin:write:domain_allows - SCOPE_admin:write > SCOPE_admin:write:domain_blocks - SCOPE_admin:write > SCOPE_admin:write:ip_blocks - SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks - SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks - """.trimIndent() - ) - - return roleHierarchyImpl - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt deleted file mode 100644 index 8f6d5b79..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -class AccountNotFoundException : ClientException { - constructor(response: MastodonApiErrorResponse) : super(response) - constructor(message: String?, response: MastodonApiErrorResponse) : super(message, response) - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse) : super( - message, - cause, - response - ) - - constructor(cause: Throwable?, response: MastodonApiErrorResponse) : super(cause, response) - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse, - ) : super(message, cause, enableSuppression, writableStackTrace, response) - - fun getTypedResponse(): MastodonApiErrorResponse = - response as MastodonApiErrorResponse - - companion object { - fun ofId(id: Long): AccountNotFoundException = AccountNotFoundException( - "id: $id was not found.", - MastodonApiErrorResponse( - NotFoundResponse( - "Record not found" - ), - 404 - ), - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt deleted file mode 100644 index 3414889a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -open class ClientException : MastodonApiException { - constructor(response: MastodonApiErrorResponse<*>) : super(response) - constructor(message: String?, response: MastodonApiErrorResponse<*>) : super(message, response) - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse<*>) : super( - message, - cause, - response - ) - - constructor(cause: Throwable?, response: MastodonApiErrorResponse<*>) : super(cause, response) - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse<*>, - ) : super(message, cause, enableSuppression, writableStackTrace, response) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt deleted file mode 100644 index c3afd55a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -@Suppress("UnnecessaryAbstractClass") -abstract class MastodonApiException : RuntimeException { - - val response: MastodonApiErrorResponse<*> - - constructor(response: MastodonApiErrorResponse<*>) : super() { - this.response = response - } - - constructor(message: String?, response: MastodonApiErrorResponse<*>) : super(message) { - this.response = response - } - - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse<*>) : super(message, cause) { - this.response = response - } - - constructor(cause: Throwable?, response: MastodonApiErrorResponse<*>) : super(cause) { - this.response = response - } - - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse<*>, - ) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) { - this.response = response - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt deleted file mode 100644 index d2d6b187..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -open class ServerException(response: MastodonApiErrorResponse<*>) : MastodonApiException(response) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt deleted file mode 100644 index 934403ec..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -class StatusNotFoundException : ClientException { - - constructor(response: MastodonApiErrorResponse) : super(response) - - constructor(message: String?, response: MastodonApiErrorResponse) : super(message, response) - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse) : super( - message, - cause, - response - ) - constructor(cause: Throwable?, response: MastodonApiErrorResponse) : super(cause, response) - - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse, - ) : super(message, cause, enableSuppression, writableStackTrace, response) - - fun getTypedResponse(): MastodonApiErrorResponse = - response as MastodonApiErrorResponse - - companion object { - fun ofId(id: Long): StatusNotFoundException = StatusNotFoundException( - "id: $id was not found.", - MastodonApiErrorResponse( - NotFoundResponse( - "Record not found" - ), - 404 - ), - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt deleted file mode 100644 index ee19ebcf..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -data class MastodonApiErrorResponse(val response: R, val statusCode: Int) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt deleted file mode 100644 index e334b04a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -import org.springframework.data.annotation.Id -import org.springframework.data.mongodb.core.mapping.Document -import java.time.Instant - -@Document -data class MastodonNotification( - @Id - val id: Long, - val userId: Long, - val type: NotificationType, - val createdAt: Instant, - val accountId: Long, - val statusId: Long?, - val reportId: Long?, - val relationshipServeranceEvent: Long? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt deleted file mode 100644 index d1e0ac54..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList - -interface MastodonNotificationRepository { - suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification - suspend fun deleteById(id: Long) - suspend fun findById(id: Long): MastodonNotification? - - @Suppress("FunctionMaxLength") - suspend fun findByUserIdAndInTypesAndInSourceActorId( - loginUser: Long, - types: List, - accountId: List, - page: Page - ): PaginationList - - suspend fun deleteByUserId(userId: Long) - suspend fun deleteByUserIdAndId(userId: Long, id: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt deleted file mode 100644 index a38cfa82..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -@Suppress("EnumEntryName", "EnumNaming", "EnumEntryNameCase") -enum class NotificationType { - mention, - status, - reblog, - follow, - follow_request, - favourite, - poll, - update, - admin_sign_up, - admin_report, - severed_relationships; - - companion object { - fun parse(string: String): NotificationType? = when (string) { - "mention" -> mention - "status" -> status - "reblog" -> reblog - "follow" -> follow - "follow_request" -> follow_request - "favourite" -> favourite - "poll" -> poll - "update" -> update - "admin.sign_up" -> admin_sign_up - "admin.report" -> admin_report - "servered_relationships" -> severed_relationships - else -> null - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt deleted file mode 100644 index 75cea253..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.exposedquery - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.mastodon.query.AccountQueryService -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.springframework.stereotype.Repository -import java.time.Instant - -@Repository -class AccountQueryServiceImpl(private val applicationConfig: ApplicationConfig) : AccountQueryService { - override suspend fun findById(accountId: Long): Account? { - val query = Actors.selectAll().where { Actors.id eq accountId } - - return query - .singleOrNull() - ?.let { toAccount(it) } - } - - override suspend fun findByIds(accountIds: List): List { - val query = Actors.selectAll().where { Actors.id inList accountIds } - - return query - .map { toAccount(it) } - } - - private fun toAccount( - resultRow: ResultRow - ): Account { - val userUrl = "${applicationConfig.url}/users/${resultRow[Actors.id]}" - - return Account( - id = resultRow[Actors.id].toString(), - username = resultRow[Actors.name], - acct = "${resultRow[Actors.name]}@${resultRow[Actors.domain]}", - url = resultRow[Actors.url], - displayName = resultRow[Actors.screenName], - note = resultRow[Actors.description], - avatar = userUrl + "/icon.jpg", - avatarStatic = userUrl + "/icon.jpg", - header = userUrl + "/header.jpg", - headerStatic = userUrl + "/header.jpg", - locked = resultRow[Actors.locked], - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = Instant.ofEpochMilli(resultRow[Actors.createdAt]).toString(), - lastStatusAt = resultRow[Actors.lastPostAt]?.toString(), - statusesCount = resultRow[Actors.postsCount], - followersCount = resultRow[Actors.followersCount], - followingCount = resultRow[Actors.followingCount], - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt deleted file mode 100644 index 3194f368..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.exposedquery - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.withPagination -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji -import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments -import dev.usbharu.hideout.core.infrastructure.exposedrepository.* -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.domain.mastodon.model.generated.Status.Visibility.* -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.selectAll -import org.springframework.stereotype.Repository -import java.time.Instant -import dev.usbharu.hideout.domain.mastodon.model.generated.CustomEmoji as MastodonEmoji - -@Suppress("IncompleteDestructuring") -@Repository -class StatusQueryServiceImpl : StatusQueryService { - override suspend fun findByPostIds(ids: List): List = findByPostIdsWithMedia(ids) - - override suspend fun findByPostIdsWithMediaIds(statusQueries: List): List { - val postIdSet = mutableSetOf() - postIdSet.addAll(statusQueries.flatMap { listOfNotNull(it.postId, it.replyId, it.repostId) }) - val mediaIdSet = mutableSetOf() - mediaIdSet.addAll(statusQueries.flatMap { it.mediaIds }) - - val emojiIdSet = mutableSetOf() - emojiIdSet.addAll(statusQueries.flatMap { it.emojiIds }) - - val postMap = Posts - .leftJoin(Actors) - .selectAll().where { Posts.id inList postIdSet } - .associate { it[Posts.id] to toStatus(it) } - val mediaMap = Media.selectAll().where { Media.id inList mediaIdSet } - .associate { - it[Media.id] to it.toMedia().toMediaAttachments() - } - - val emojiMap = CustomEmojis.selectAll().where { CustomEmojis.id inList emojiIdSet }.associate { - it[CustomEmojis.id] to it.toCustomEmoji().toMastodonEmoji() - } - return statusQueries.mapNotNull { statusQuery -> - postMap[statusQuery.postId]?.copy( - inReplyToId = statusQuery.replyId?.toString(), - inReplyToAccountId = postMap[statusQuery.replyId]?.account?.id, - reblog = postMap[statusQuery.repostId], - mediaAttachments = statusQuery.mediaIds.mapNotNull { mediaMap[it] }, - emojis = statusQuery.emojiIds.mapNotNull { emojiMap[it] } - ) - } - } - - override suspend fun accountsStatus( - accountId: Long, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - includeFollowers: Boolean, - page: Page - ): PaginationList { - val query = Posts - .leftJoin(PostsMedia) - .leftJoin(Actors) - .leftJoin(Media) - .selectAll().where { Posts.actorId eq accountId } - - if (onlyMedia) { - query.andWhere { PostsMedia.mediaId.isNotNull() } - } - if (excludeReplies) { - query.andWhere { Posts.replyId.isNotNull() } - } - if (excludeReblogs) { - query.andWhere { Posts.repostId.isNotNull() } - } - if (includeFollowers) { - query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal, private.ordinal) } - } else { - query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal) } - } - - val pairs = query - .withPagination(page, Posts.id) - .groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - } - ) to it.first()[Posts.repostId] - } - - val statuses = resolveReplyAndRepost(pairs) - return PaginationList( - statuses, - statuses.firstOrNull()?.id?.toLongOrNull(), - statuses.lastOrNull()?.id?.toLongOrNull() - ) - } - - override suspend fun findByPostId(id: Long): Status? { - val map = Posts - .leftJoin(PostsMedia) - .leftJoin(Actors) - .leftJoin(Media) - .selectAll() - .where { Posts.id eq id } - .groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - }, - emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() } - ) to it.first()[Posts.repostId] - } - return resolveReplyAndRepost(map).singleOrNull() - } - - private fun resolveReplyAndRepost(pairs: List>): List { - val statuses = pairs.map { it.first } - return pairs - .map { - if (it.second != null) { - it.first.copy(reblog = statuses.find { (id) -> id == it.second.toString() }) - } else { - it.first - } - } - .map { - if (it.inReplyToId != null) { - println("statuses trace: $statuses") - println("inReplyToId trace: ${it.inReplyToId}") - it.copy(inReplyToAccountId = statuses.find { (id) -> id == it.inReplyToId }?.account?.id) - } else { - it - } - } - } - - private suspend fun findByPostIdsWithMedia(ids: List): List { - val pairs = Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .leftJoin(CustomEmojis) - .leftJoin(Actors) - .leftJoin(Media) - .selectAll().where { Posts.id inList ids } - .groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - }, - emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() } - ) to it.first()[Posts.repostId] - } - return resolveReplyAndRepost(pairs) - } -} - -private fun CustomEmoji.toMastodonEmoji(): MastodonEmoji = MastodonEmoji( - shortcode = this.name, - url = this.url, - staticUrl = this.url, - visibleInPicker = true, - category = this.category.orEmpty() -) - -private fun toStatus(it: ResultRow) = Status( - id = it[Posts.id].toString(), - uri = it[Posts.apId], - createdAt = Instant.ofEpochMilli(it[Posts.createdAt]).toString(), - account = Account( - id = it[Actors.id].toString(), - username = it[Actors.name], - acct = "${it[Actors.name]}@${it[Actors.domain]}", - url = it[Actors.url], - displayName = it[Actors.screenName], - note = it[Actors.description], - avatar = it[Actors.url] + "/icon.jpg", - avatarStatic = it[Actors.url] + "/icon.jpg", - header = it[Actors.url] + "/header.jpg", - headerStatic = it[Actors.url] + "/header.jpg", - locked = it[Actors.locked], - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = Instant.ofEpochMilli(it[Actors.createdAt]).toString(), - lastStatusAt = it[Actors.lastPostAt]?.toString(), - statusesCount = it[Actors.postsCount], - followersCount = it[Actors.followersCount], - followingCount = it[Actors.followingCount], - noindex = false, - moved = false, - suspendex = false, - limited = false - ), - content = it[Posts.text], - visibility = when (it[Posts.visibility]) { - 0 -> public - 1 -> unlisted - 2 -> private - 3 -> direct - else -> public - }, - sensitive = it[Posts.sensitive], - spoilerText = it[Posts.overview].orEmpty(), - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = it[Posts.apId], - inReplyToId = it[Posts.replyId]?.toString(), - inReplyToAccountId = null, - language = null, - text = it[Posts.text], - editedAt = null -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt deleted file mode 100644 index 7f928b08..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.withPagination -import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.stereotype.Repository - -@Repository -@Qualifier("jdbc") -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true) -class ExposedMastodonNotificationRepository : MastodonNotificationRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification = query { - val singleOrNull = - MastodonNotifications.selectAll().where { MastodonNotifications.id eq mastodonNotification.id } - .singleOrNull() - if (singleOrNull == null) { - MastodonNotifications.insert { - it[id] = mastodonNotification.id - it[type] = mastodonNotification.type.name - it[createdAt] = mastodonNotification.createdAt - it[accountId] = mastodonNotification.accountId - it[statusId] = mastodonNotification.statusId - it[reportId] = mastodonNotification.reportId - it[relationshipServeranceEventId] = - mastodonNotification.relationshipServeranceEvent - } - } else { - MastodonNotifications.update({ MastodonNotifications.id eq mastodonNotification.id }) { - it[type] = mastodonNotification.type.name - it[createdAt] = mastodonNotification.createdAt - it[accountId] = mastodonNotification.accountId - it[statusId] = mastodonNotification.statusId - it[reportId] = mastodonNotification.reportId - it[relationshipServeranceEventId] = - mastodonNotification.relationshipServeranceEvent - } - } - mastodonNotification - } - - override suspend fun deleteById(id: Long): Unit = query { - MastodonNotifications.deleteWhere { - MastodonNotifications.id eq id - } - } - - override suspend fun findById(id: Long): MastodonNotification? = query { - MastodonNotifications.selectAll().where { MastodonNotifications.id eq id }.singleOrNull() - ?.toMastodonNotification() - } - - override suspend fun findByUserIdAndInTypesAndInSourceActorId( - loginUser: Long, - types: List, - accountId: List, - page: Page - ): PaginationList = query { - val query = MastodonNotifications.selectAll().where { MastodonNotifications.userId eq loginUser } - val result = query.withPagination(page, MastodonNotifications.id) - - return@query PaginationList(result.map { it.toMastodonNotification() }, result.next, result.prev) - } - - override suspend fun deleteByUserId(userId: Long) { - MastodonNotifications.deleteWhere { - MastodonNotifications.userId eq userId - } - } - - override suspend fun deleteByUserIdAndId(userId: Long, id: Long) { - MastodonNotifications.deleteWhere { - MastodonNotifications.userId eq userId and (MastodonNotifications.id eq id) - } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedMastodonNotificationRepository::class.java) - } -} - -fun ResultRow.toMastodonNotification(): MastodonNotification = MastodonNotification( - id = this[MastodonNotifications.id], - userId = this[MastodonNotifications.userId], - type = NotificationType.valueOf(this[MastodonNotifications.type]), - createdAt = this[MastodonNotifications.createdAt], - accountId = this[MastodonNotifications.accountId], - statusId = this[MastodonNotifications.statusId], - reportId = this[MastodonNotifications.reportId], - relationshipServeranceEvent = this[MastodonNotifications.relationshipServeranceEventId], -) - -object MastodonNotifications : Table("mastodon_notifications") { - val id = long("id") - val userId = long("user_id") - val type = varchar("type", 100) - val createdAt = timestamp("created_at") - val accountId = long("account_id") - val statusId = long("status_id").nullable() - val reportId = long("report_id").nullable() - val relationshipServeranceEventId = long("relationship_serverance_event_id").nullable() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt deleted file mode 100644 index e95d204f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.mongorepository - -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import org.springframework.data.mongodb.repository.MongoRepository - -interface MongoMastodonNotificationRepository : MongoRepository { - - fun deleteByUserId(userId: Long): Long - - fun deleteByIdAndUserId(id: Long, userId: Long): Long -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt deleted file mode 100644 index 4fd243e9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.mongorepository - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.data.domain.Sort -import org.springframework.data.mongodb.core.MongoTemplate -import org.springframework.data.mongodb.core.query.Criteria -import org.springframework.data.mongodb.core.query.Query -import org.springframework.stereotype.Repository -import kotlin.jvm.optionals.getOrNull - -@Repository -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false) -class MongoMastodonNotificationRepositoryWrapper( - private val mongoMastodonNotificationRepository: MongoMastodonNotificationRepository, - private val mongoTemplate: MongoTemplate -) : - MastodonNotificationRepository { - override suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification = - mongoMastodonNotificationRepository.save(mastodonNotification) - - override suspend fun deleteById(id: Long) = mongoMastodonNotificationRepository.deleteById(id) - - override suspend fun findById(id: Long): MastodonNotification? = - mongoMastodonNotificationRepository.findById(id).getOrNull() - - override suspend fun findByUserIdAndInTypesAndInSourceActorId( - loginUser: Long, - types: List, - accountId: List, - page: Page - ): PaginationList { - val query = Query() - - page.limit?.let { query.limit(it) } - - val mastodonNotifications = if (page.minId != null) { - query.with(Sort.by(Sort.Direction.ASC, "id")) - page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) } - page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } - mongoTemplate.find(query, MastodonNotification::class.java).reversed() - } else { - query.with(Sort.by(Sort.Direction.DESC, "id")) - page.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) } - page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } - mongoTemplate.find(query, MastodonNotification::class.java) - } - - return PaginationList( - mastodonNotifications, - mastodonNotifications.firstOrNull()?.id, - mastodonNotifications.lastOrNull()?.id - ) - } - - override suspend fun deleteByUserId(userId: Long) { - mongoMastodonNotificationRepository.deleteByUserId(userId) - } - - override suspend fun deleteByUserIdAndId(userId: Long, id: Long) { - mongoMastodonNotificationRepository.deleteByIdAndUserId(id, userId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt deleted file mode 100644 index b154f52b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.springweb - -import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse -import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponse -import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponseDetails -import dev.usbharu.hideout.mastodon.domain.exception.AccountNotFoundException -import dev.usbharu.hideout.mastodon.domain.exception.StatusNotFoundException -import dev.usbharu.hideout.mastodon.interfaces.api.account.MastodonAccountApiController -import dev.usbharu.hideout.mastodon.interfaces.api.apps.MastodonAppsApiController -import dev.usbharu.hideout.mastodon.interfaces.api.filter.MastodonFilterApiController -import dev.usbharu.hideout.mastodon.interfaces.api.instance.MastodonInstanceApiController -import dev.usbharu.hideout.mastodon.interfaces.api.media.MastodonMediaApiController -import dev.usbharu.hideout.mastodon.interfaces.api.notification.MastodonNotificationApiController -import dev.usbharu.hideout.mastodon.interfaces.api.status.MastodonStatusesApiContoller -import dev.usbharu.hideout.mastodon.interfaces.api.timeline.MastodonTimelineApiController -import org.slf4j.LoggerFactory -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.validation.BindException -import org.springframework.validation.FieldError -import org.springframework.web.bind.annotation.ControllerAdvice -import org.springframework.web.bind.annotation.ExceptionHandler - -@ControllerAdvice( - assignableTypes = [ - MastodonAccountApiController::class, - MastodonAppsApiController::class, - MastodonFilterApiController::class, - MastodonInstanceApiController::class, - MastodonMediaApiController::class, - MastodonNotificationApiController::class, - MastodonStatusesApiContoller::class, - MastodonTimelineApiController::class - ] -) -class MastodonApiControllerAdvice { - - @ExceptionHandler(BindException::class) - fun handleException(ex: BindException): ResponseEntity { - logger.debug("Failed bind entity.", ex) - - val details = mutableMapOf>() - - ex.allErrors.forEach { - val defaultMessage = it.defaultMessage - when { - it is FieldError -> { - val code = when (it.code) { - "Email" -> "ERR_INVALID" - "Pattern" -> "ERR_INVALID" - else -> "ERR_INVALID" - } - details.getOrPut(it.field) { - mutableListOf() - }.add(UnprocessableEntityResponseDetails(code, defaultMessage.orEmpty())) - } - - defaultMessage?.startsWith("Parameter specified as non-null is null:") == true -> { - val parameter = defaultMessage.substringAfterLast("parameter ") - - details.getOrPut(parameter) { - mutableListOf() - }.add(UnprocessableEntityResponseDetails("ERR_BLANK", "can't be blank")) - } - - else -> { - logger.warn("Unknown validation error", ex) - } - } - } - - val message = details.map { - it.key + " " + it.value.joinToString { responseDetails -> responseDetails.description } - }.joinToString() - - return ResponseEntity.unprocessableEntity() - .body(UnprocessableEntityResponse(message, details)) - } - - @ExceptionHandler(StatusNotFoundException::class) - fun handleException(ex: StatusNotFoundException): ResponseEntity { - logger.warn("Status not found.", ex) - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response) - } - - @ExceptionHandler(AccountNotFoundException::class) - fun handleException(ex: AccountNotFoundException): ResponseEntity { - logger.warn("Account not found.", ex) - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response) - } - - companion object { - private val logger = LoggerFactory.getLogger(MastodonApiControllerAdvice::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt deleted file mode 100644 index a2e391af..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.account - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader -import dev.usbharu.hideout.controller.mastodon.generated.AccountApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.core.service.user.UserCreateDto -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.service.account.AccountApiService -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.runBlocking -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import java.net.URI - -@Controller -class MastodonAccountApiController( - private val accountApiService: AccountApiService, - private val transaction: Transaction, - private val loginUserContextHolder: LoginUserContextHolder, - private val applicationConfig: ApplicationConfig -) : AccountApi { - - override suspend fun apiV1AccountsIdFollowPost( - id: String, - followRequestBody: FollowRequestBody? - ): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - return ResponseEntity.ok(accountApiService.follow(userid, id.toLong())) - } - - override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity = - ResponseEntity.ok(accountApiService.account(id.toLong())) - - override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity = ResponseEntity( - accountApiService.verifyCredentials(loginUserContextHolder.getLoginUserId()), - HttpStatus.OK - ) - - override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity { - transaction.transaction { - accountApiService.registerAccount( - UserCreateDto( - accountsCreateRequest.username, - accountsCreateRequest.username, - "", - accountsCreateRequest.password - ) - ) - } - val httpHeaders = HttpHeaders() - httpHeaders.location = URI("/users/${accountsCreateRequest.username}") - return ResponseEntity(Unit, httpHeaders, HttpStatus.FOUND) - } - - override fun apiV1AccountsIdStatusesGet( - id: String, - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String? - ): ResponseEntity> = runBlocking { - val userid = loginUserContextHolder.getLoginUserIdOrNull() - val statuses = accountApiService.accountsStatuses( - userid = id.toLong(), - onlyMedia = onlyMedia, - excludeReplies = excludeReplies, - excludeReblogs = excludeReblogs, - pinned = pinned, - tagged = tagged, - loginUser = userid, - page = Page.of( - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - minId?.toLongOrNull(), - limit.coerceIn(0, 80) - ) - ) - val httpHeader = statuses.toHttpHeader( - { "${applicationConfig.url}/api/v1/accounts/$id/statuses?min_id=$it" }, - { "${applicationConfig.url}/api/v1/accounts/$id/statuses?max_id=$it" }, - ) - - if (httpHeader != null) { - return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(statuses.asFlow()) - } - - ResponseEntity.ok(statuses.asFlow()) - } - - override fun apiV1AccountsRelationshipsGet( - id: List?, - withSuspended: Boolean - ): ResponseEntity> = runBlocking { - val userid = loginUserContextHolder.getLoginUserId() - - ResponseEntity.ok( - accountApiService.relationships(userid, id.orEmpty().mapNotNull { it.toLongOrNull() }, withSuspended) - .asFlow() - ) - } - - override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val block = accountApiService.block(userid, id.toLong()) - - return ResponseEntity.ok(block) - } - - override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val unblock = accountApiService.unblock(userid, id.toLong()) - - return ResponseEntity.ok(unblock) - } - - override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val unfollow = accountApiService.unfollow(userid, id.toLong()) - - return ResponseEntity.ok(unfollow) - } - - override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val removeFromFollowers = accountApiService.removeFromFollowers(userid, id.toLong()) - - return ResponseEntity.ok(removeFromFollowers) - } - - override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val removeFromFollowers = accountApiService.updateProfile(userid, updateCredentials) - - return ResponseEntity.ok(removeFromFollowers) - } - - override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val acceptFollowRequest = accountApiService.acceptFollowRequest(userid, accountId.toLong()) - - return ResponseEntity.ok(acceptFollowRequest) - } - - override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val rejectFollowRequest = accountApiService.rejectFollowRequest(userid, accountId.toLong()) - - return ResponseEntity.ok(rejectFollowRequest) - } - - override fun apiV1FollowRequestsGet(maxId: String?, sinceId: String?, limit: Int?): ResponseEntity> = - runBlocking { - val userid = loginUserContextHolder.getLoginUserId() - - val followRequests = accountApiService.followRequests( - userid, - false, - Page.PageByMaxId( - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - limit?.coerceIn(0, 80) ?: 40 - ) - - ) - - val httpHeader = followRequests.toHttpHeader( - { "${applicationConfig.url}/api/v1/follow_requests?max_id=$it" }, - { "${applicationConfig.url}/api/v1/follow_requests?min_id=$it" }, - ) - - if (httpHeader != null) { - return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(followRequests.asFlow()) - } - - ResponseEntity.ok(followRequests.asFlow()) - } - - override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val mute = accountApiService.mute(userid, id.toLong()) - - return ResponseEntity.ok(mute) - } - - override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val unmute = accountApiService.unmute(userid, id.toLong()) - - return ResponseEntity.ok(unmute) - } - - override fun apiV1MutesGet(maxId: String?, sinceId: String?, limit: Int?): ResponseEntity> = - runBlocking { - val userid = loginUserContextHolder.getLoginUserId() - - val mutes = - accountApiService.mutesAccount( - userid, - Page.PageByMaxId(maxId?.toLongOrNull(), sinceId?.toLongOrNull(), limit?.coerceIn(0, 80) ?: 40) - ) - - val httpHeader = mutes.toHttpHeader( - { "${applicationConfig.url}/api/v1/mutes?max_id=$it" }, - { "${applicationConfig.url}/api/v1/mutes?since_id=$it" }, - ) - - if (httpHeader != null) { - return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(mutes.asFlow()) - } - - return@runBlocking ResponseEntity.ok(mutes.asFlow()) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt deleted file mode 100644 index 8424d6d9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.apps - -import dev.usbharu.hideout.controller.mastodon.generated.AppApi -import dev.usbharu.hideout.domain.mastodon.model.generated.Application -import dev.usbharu.hideout.domain.mastodon.model.generated.AppsRequest -import dev.usbharu.hideout.mastodon.service.app.AppApiService -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RequestParam - -@Controller -class MastodonAppsApiController(private val appApiService: AppApiService) : AppApi { - override suspend fun apiV1AppsPost(appsRequest: AppsRequest): ResponseEntity { - return ResponseEntity( - appApiService.createApp(appsRequest), - HttpStatus.OK - ) - } - - @RequestMapping( - method = [RequestMethod.POST], - value = ["/api/v1/apps"], - produces = ["application/json"], - consumes = ["application/x-www-form-urlencoded"] - ) - suspend fun apiV1AppsPost(@RequestParam map: Map): ResponseEntity { - val appsRequest = - AppsRequest(map.getValue("client_name"), map.getValue("redirect_uris"), map["scopes"], map["website"]) - return ResponseEntity( - appApiService.createApp(appsRequest), - HttpStatus.OK - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt deleted file mode 100644 index 28f5d3df..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.filter - -import dev.usbharu.hideout.controller.mastodon.generated.FilterApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.service.filter.MastodonFilterApiService -import kotlinx.coroutines.flow.Flow -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonFilterApiController( - private val mastodonFilterApiService: MastodonFilterApiService, - private val loginUserContextHolder: LoginUserContextHolder -) : FilterApi { - - override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteV1FilterById(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV1FiltersIdGet( - id: String - ): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.getV1FilterById( - loginUserContextHolder.getLoginUserId(), - id.toLong() - ) - ) - } - - override suspend fun apiV1FiltersIdPut( - id: String, - phrase: String?, - context: List?, - irreversible: Boolean?, - wholeWord: Boolean?, - expiresIn: Int? - ): ResponseEntity = super.apiV1FiltersIdPut(id, phrase, context, irreversible, wholeWord, expiresIn) - - override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.createByV1Filter(loginUserContextHolder.getLoginUserId(), v1FilterPostRequest) - ) - } - - override suspend fun apiV2FiltersFilterIdKeywordsPost( - filterId: String, - filterKeywordsPostRequest: FilterKeywordsPostRequest - ): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.addKeyword( - loginUserContextHolder.getLoginUserId(), - filterId.toLong(), - filterKeywordsPostRequest - ) - ) - } - - override suspend fun apiV2FiltersFilterIdStatusesPost( - filterId: String, - filterStatusRequest: FilterStatusRequest - ): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.addFilterStatus( - loginUserContextHolder.getLoginUserId(), - filterId.toLong(), - filterStatusRequest - ) - ) - } - - override fun apiV1FiltersGet(): ResponseEntity> = - ResponseEntity.ok(mastodonFilterApiService.v1Filters(loginUserContextHolder.getLoginUserId())) - - override fun apiV2FiltersFilterIdKeywordsGet(filterId: String): ResponseEntity> { - return ResponseEntity.ok( - mastodonFilterApiService.filterKeywords( - loginUserContextHolder.getLoginUserId(), - filterId.toLong() - ) - ) - } - - override fun apiV2FiltersFilterIdStatusesGet(filterId: String): ResponseEntity> { - return ResponseEntity.ok( - mastodonFilterApiService.filterStatuses( - loginUserContextHolder.getLoginUserId(), - filterId.toLong() - ) - ) - } - - override fun apiV2FiltersGet(): ResponseEntity> = - ResponseEntity.ok(mastodonFilterApiService.filters(loginUserContextHolder.getLoginUserId())) - - override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteById(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity = - ResponseEntity.ok(mastodonFilterApiService.getById(loginUserContextHolder.getLoginUserId(), id.toLong())) - - override suspend fun apiV2FiltersIdPut( - id: String, - title: String?, - context: List?, - filterAction: String?, - expiresIn: Int?, - keywordsAttributes: List? - ): ResponseEntity = - super.apiV2FiltersIdPut(id, title, context, filterAction, expiresIn, keywordsAttributes) - - override suspend fun apiV2FiltersKeywordsIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteKeyword(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV2FiltersKeywordsIdGet(id: String): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.getKeywordById( - loginUserContextHolder.getLoginUserId(), - id.toLong() - ) - ) - } - - override suspend fun apiV2FiltersKeywordsIdPut( - id: String, - keyword: String?, - wholeWord: Boolean?, - regex: Boolean? - ): ResponseEntity = super.apiV2FiltersKeywordsIdPut(id, keyword, wholeWord, regex) - - override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity = - ResponseEntity.ok( - mastodonFilterApiService.createFilter( - loginUserContextHolder.getLoginUserId(), - filterPostRequest - ) - ) - - override suspend fun apiV2FiltersStatusesIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteFilterStatusById(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV2FiltersStatusesIdGet(id: String): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.getFilterStatusById( - loginUserContextHolder.getLoginUserId(), - id.toLong() - ) - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt deleted file mode 100644 index 220625ce..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.instance - -import dev.usbharu.hideout.controller.mastodon.generated.InstanceApi -import dev.usbharu.hideout.domain.mastodon.model.generated.V1Instance -import dev.usbharu.hideout.mastodon.service.instance.InstanceApiService -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonInstanceApiController(private val instanceApiService: InstanceApiService) : InstanceApi { - override suspend fun apiV1InstanceGet(): ResponseEntity = - ResponseEntity(instanceApiService.v1Instance(), HttpStatus.OK) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt deleted file mode 100644 index adcdb770..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.media - -import dev.usbharu.hideout.controller.mastodon.generated.MediaApi -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.service.media.MediaApiService -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import org.springframework.web.multipart.MultipartFile - -@Controller -class MastodonMediaApiController(private val mediaApiService: MediaApiService) : MediaApi { - override suspend fun apiV1MediaPost( - file: MultipartFile, - thumbnail: MultipartFile?, - description: String?, - focus: String? - ): ResponseEntity { - return ResponseEntity.ok( - mediaApiService.postMedia( - MediaRequest( - file, - thumbnail, - description, - focus - ) - ) - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt deleted file mode 100644 index d9637193..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.media - -import org.springframework.web.multipart.MultipartFile - -data class MediaRequest( - val file: MultipartFile, - val thumbnail: MultipartFile?, - val description: String?, - val focus: String? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt deleted file mode 100644 index 9b0a466a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.notification - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader -import dev.usbharu.hideout.controller.mastodon.generated.NotificationsApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import dev.usbharu.hideout.mastodon.service.notification.NotificationApiService -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.runBlocking -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonNotificationApiController( - private val loginUserContextHolder: LoginUserContextHolder, - private val notificationApiService: NotificationApiService, - private val applicationConfig: ApplicationConfig -) : NotificationsApi { - override suspend fun apiV1NotificationsClearPost(): ResponseEntity { - notificationApiService.clearAll(loginUserContextHolder.getLoginUserId()) - return ResponseEntity.ok(null) - } - - override fun apiV1NotificationsGet( - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int?, - types: List?, - excludeTypes: List?, - accountId: List? - ): ResponseEntity> = runBlocking { - val notifications = notificationApiService.notifications( - loginUser = loginUserContextHolder.getLoginUserId(), - types = types.orEmpty().mapNotNull { NotificationType.parse(it) }, - excludeTypes = excludeTypes.orEmpty().mapNotNull { NotificationType.parse(it) }, - accountId = accountId.orEmpty().mapNotNull { it.toLongOrNull() }, - page = Page.of( - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - minId?.toLongOrNull(), - limit?.coerceIn(0, 80) ?: 40 - ) - ) - - val httpHeader = notifications.toHttpHeader( - { "${applicationConfig.url}/api/v1/notifications?min_id=$it" }, - { "${applicationConfig.url}/api/v1/notifications?max_id=$it" } - ) ?: return@runBlocking ResponseEntity.ok( - notifications.asFlow() - ) - - ResponseEntity.ok().header("Link", httpHeader).body(notifications.asFlow()) - } - - override suspend fun apiV1NotificationsIdDismissPost(id: String): ResponseEntity { - notificationApiService.dismiss(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok(null) - } - - override suspend fun apiV1NotificationsIdGet(id: String): ResponseEntity { - val notification = notificationApiService.fingById(loginUserContextHolder.getLoginUserId(), id.toLong()) - - return ResponseEntity.ok(notification) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt deleted file mode 100644 index 20858ec6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -import dev.usbharu.hideout.controller.mastodon.generated.StatusApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.service.status.StatusesApiService -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonStatusesApiContoller( - private val statusesApiService: StatusesApiService, - private val loginUserContextHolder: LoginUserContextHolder -) : StatusApi { - override suspend fun apiV1StatusesPost( - devUsbharuHideoutDomainModelMastodonStatusesRequest: StatusesRequest - ): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - return ResponseEntity( - statusesApiService.postStatus( - devUsbharuHideoutDomainModelMastodonStatusesRequest, - userid - ), - HttpStatus.OK - ) - } - - override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity { - val uid = - loginUserContextHolder.getLoginUserId() - - return ResponseEntity.ok(statusesApiService.removeEmojiReactions(id.toLong(), uid, emoji)) - } - - override suspend fun apiV1StatusesIdEmojiReactionsEmojiPut(id: String, emoji: String): ResponseEntity { - val uid = - loginUserContextHolder.getLoginUserId() - - return ResponseEntity.ok(statusesApiService.emojiReactions(id.toLong(), uid, emoji)) - } - - override suspend fun apiV1StatusesIdGet(id: String): ResponseEntity { - val uid = loginUserContextHolder.getLoginUserIdOrNull() - - return ResponseEntity.ok(statusesApiService.findById(id.toLong(), uid)) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt deleted file mode 100644 index 19d2cc8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -data class StatusQuery( - val postId: Long, - val replyId: Long?, - val repostId: Long?, - val mediaIds: List, - val emojiIds: List -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt deleted file mode 100644 index 1005680d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -import com.fasterxml.jackson.annotation.JsonProperty -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequestPoll -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest.Visibility.* - -@Suppress("VariableNaming", "EnumEntryName") -class StatusesRequest { - @JsonProperty("status") - var status: String? = null - - @JsonProperty("media_ids") - var media_ids: List = emptyList() - - @JsonProperty("poll") - var poll: StatusesRequestPoll? = null - - @JsonProperty("in_reply_to_id") - var in_reply_to_id: String? = null - - @JsonProperty("sensitive") - var sensitive: Boolean? = null - - @JsonProperty("spoiler_text") - var spoiler_text: String? = null - - @JsonProperty("visibility") - var visibility: Visibility? = null - - @JsonProperty("language") - var language: String? = null - - @JsonProperty("scheduled_at") - var scheduled_at: String? = null - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is StatusesRequest) return false - - if (status != other.status) return false - if (media_ids != other.media_ids) return false - if (poll != other.poll) return false - if (in_reply_to_id != other.in_reply_to_id) return false - if (sensitive != other.sensitive) return false - if (spoiler_text != other.spoiler_text) return false - if (visibility != other.visibility) return false - if (language != other.language) return false - if (scheduled_at != other.scheduled_at) return false - - return true - } - - override fun hashCode(): Int { - var result = status?.hashCode() ?: 0 - result = 31 * result + media_ids.hashCode() - result = 31 * result + (poll?.hashCode() ?: 0) - result = 31 * result + (in_reply_to_id?.hashCode() ?: 0) - result = 31 * result + (sensitive?.hashCode() ?: 0) - result = 31 * result + (spoiler_text?.hashCode() ?: 0) - result = 31 * result + (visibility?.hashCode() ?: 0) - result = 31 * result + (language?.hashCode() ?: 0) - result = 31 * result + (scheduled_at?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "StatusesRequest(" + - "status=$status, " + - "media_ids=$media_ids, " + - "poll=$poll, " + - "in_reply_to_id=$in_reply_to_id, " + - "sensitive=$sensitive, " + - "spoiler_text=$spoiler_text, " + - "visibility=$visibility, " + - "language=$language, " + - "scheduled_at=$scheduled_at" + - ")" - } - - @Suppress("EnumNaming", "EnumEntryNameCase") - enum class Visibility { - `public`, - unlisted, - private, - direct - } -} - -fun StatusesRequest.Visibility?.toPostVisibility(): Visibility { - return when (this) { - public -> Visibility.PUBLIC - unlisted -> Visibility.UNLISTED - private -> Visibility.FOLLOWERS - direct -> Visibility.DIRECT - null -> Visibility.PUBLIC - } -} - -fun StatusesRequest.Visibility?.toStatusVisibility(): Status.Visibility { - return when (this) { - public -> Status.Visibility.public - unlisted -> Status.Visibility.unlisted - private -> Status.Visibility.private - direct -> Status.Visibility.direct - null -> Status.Visibility.public - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt deleted file mode 100644 index 7bbc8f8d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.timeline - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader -import dev.usbharu.hideout.controller.mastodon.generated.TimelineApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.service.timeline.TimelineApiService -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.runBlocking -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonTimelineApiController( - private val timelineApiService: TimelineApiService, - private val loginUserContextHolder: LoginUserContextHolder, - private val applicationConfig: ApplicationConfig, -) : TimelineApi { - override fun apiV1TimelinesHomeGet( - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int? - ): ResponseEntity> = runBlocking { - val homeTimeline = timelineApiService.homeTimeline( - userId = loginUserContextHolder.getLoginUserId(), - page = Page.of( - maxId = maxId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - limit = limit?.coerceIn(0, 80) ?: 40 - ) - ) - - val httpHeader = homeTimeline.toHttpHeader( - { "${applicationConfig.url}/api/v1/home?max_id=$it" }, - { "${applicationConfig.url}/api/v1/home?min_id=$it" } - ) ?: return@runBlocking ResponseEntity( - homeTimeline.asFlow(), - HttpStatus.OK - ) - ResponseEntity.ok().header("Link", httpHeader).body(homeTimeline.asFlow()) - } - - override fun apiV1TimelinesPublicGet( - local: Boolean?, - remote: Boolean?, - onlyMedia: Boolean?, - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int? - ): ResponseEntity> = runBlocking { - val publicTimeline = timelineApiService.publicTimeline( - localOnly = local ?: false, - remoteOnly = remote ?: false, - mediaOnly = onlyMedia ?: false, - page = Page.of( - maxId = maxId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - limit = limit?.coerceIn(0, 80) ?: 40 - ) - ) - - val httpHeader = publicTimeline.toHttpHeader( - { "${applicationConfig.url}/api/v1/public?max_id=$it" }, - { "${applicationConfig.url}/api/v1/public?min_id=$it" } - ) ?: return@runBlocking ResponseEntity( - publicTimeline.asFlow(), - HttpStatus.OK - ) - ResponseEntity.ok().header("Link", httpHeader).body(publicTimeline.asFlow()) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt deleted file mode 100644 index 61b49950..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.query - -import dev.usbharu.hideout.domain.mastodon.model.generated.Account - -interface AccountQueryService { - suspend fun findById(accountId: Long): Account? - suspend fun findByIds(accountIds: List): List -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt deleted file mode 100644 index e5640509..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.query - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery - -interface StatusQueryService { - suspend fun findByPostIds(ids: List): List - suspend fun findByPostIdsWithMediaIds(statusQueries: List): List - - @Suppress("LongParameterList") - suspend fun accountsStatus( - accountId: Long, - onlyMedia: Boolean = false, - excludeReplies: Boolean = false, - excludeReblogs: Boolean = false, - pinned: Boolean = false, - tagged: String?, - includeFollowers: Boolean = false, - page: Page - ): PaginationList - - suspend fun findByPostId(id: Long): Status? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt deleted file mode 100644 index 0e3afc2f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.account - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UpdateUserDto -import dev.usbharu.hideout.core.service.user.UserCreateDto -import dev.usbharu.hideout.core.service.user.UserService -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.domain.exception.AccountNotFoundException -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import kotlin.math.min - -@Service -@Suppress("TooManyFunctions") -interface AccountApiService { - - @Suppress("LongParameterList") - suspend fun accountsStatuses( - userid: Long, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - loginUser: Long?, - page: Page - ): PaginationList - - suspend fun verifyCredentials(userid: Long): CredentialAccount - suspend fun registerAccount(userCreateDto: UserCreateDto): Unit - suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship - suspend fun account(id: Long): Account - suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List - - /** - * ブロック操作を行う - * - * @param userid ブロック操作を行ったユーザーid - * @param target ブロック対象のユーザーid - * @return ブロック後のブロック対象ユーザーとの[Relationship] - */ - suspend fun block(userid: Long, target: Long): Relationship - suspend fun unblock(userid: Long, target: Long): Relationship - suspend fun unfollow(userid: Long, target: Long): Relationship - suspend fun removeFromFollowers(userid: Long, target: Long): Relationship - suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account - - suspend fun followRequests( - loginUser: Long, - withIgnore: Boolean, - pageByMaxId: Page.PageByMaxId - ): PaginationList - - suspend fun acceptFollowRequest(loginUser: Long, target: Long): Relationship - suspend fun rejectFollowRequest(loginUser: Long, target: Long): Relationship - suspend fun mute(userid: Long, target: Long): Relationship - suspend fun unmute(userid: Long, target: Long): Relationship - suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList -} - -@Service -class AccountApiServiceImpl( - private val accountService: AccountService, - private val transaction: Transaction, - private val userService: UserService, - private val statusQueryService: StatusQueryService, - private val relationshipService: RelationshipService, - private val relationshipRepository: RelationshipRepository, - private val mediaService: MediaService -) : - AccountApiService { - - override suspend fun accountsStatuses( - userid: Long, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - loginUser: Long?, - page: Page - ): PaginationList { - val canViewFollowers = if (loginUser == null) { - false - } else if (loginUser == userid) { - true - } else { - transaction.transaction { - isFollowing(loginUser, userid) - } - } - - return transaction.transaction { - statusQueryService.accountsStatus( - accountId = userid, - onlyMedia = onlyMedia, - excludeReplies = excludeReplies, - excludeReblogs = excludeReblogs, - pinned = pinned, - tagged = tagged, - includeFollowers = canViewFollowers, - page = page - ) - } - } - - override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction { - userService.updateUserStatistics(userid) - - val account = accountService.findById(userid) - from(account) - } - - override suspend fun registerAccount(userCreateDto: UserCreateDto) { - userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password)) - } - - override suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship = transaction.transaction { - relationshipService.followRequest(loginUser, followTargetUserId) - - return@transaction fetchRelationship(loginUser, followTargetUserId) - } - - override suspend fun account(id: Long): Account { - return try { - transaction.transaction { - userService.updateUserStatistics(id) - return@transaction accountService.findById(id) - } - } catch (_: UserNotFoundException) { - throw AccountNotFoundException.ofId(id) - } - } - - override suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List = - transaction.transaction { - if (id.isEmpty()) { - return@transaction emptyList() - } - - logger.warn("id is too long! ({}) truncate to 20", id.size) - - val subList = id.subList(0, min(id.size, 20)) - - return@transaction subList.map { - fetchRelationship(userid, it) - } - } - - override suspend fun block(userid: Long, target: Long) = transaction.transaction { - relationshipService.block(userid, target) - - fetchRelationship(userid, target) - } - - override suspend fun unblock(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.unblock(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun unfollow(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.unfollow(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun removeFromFollowers(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.rejectFollowRequest(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account = - transaction.transaction { - val avatarMedia = if (updateCredentials?.avatar != null) { - mediaService.uploadLocalMedia( - MediaRequest( - updateCredentials.avatar, - null, - null, - null - ) - ) - } else { - null - } - - val headerMedia = if (updateCredentials?.header != null) { - mediaService.uploadLocalMedia( - MediaRequest( - updateCredentials.header, - null, - null, - null - ) - ) - } else { - null - } - - val account = accountService.findById(userid) - - val updateUserDto = UpdateUserDto( - screenName = updateCredentials?.displayName ?: account.displayName, - description = updateCredentials?.note ?: account.note, - avatarMedia = avatarMedia, - headerMedia = headerMedia, - locked = updateCredentials?.locked ?: account.locked, - autoAcceptFolloweeFollowRequest = false - ) - userService.updateUser(userid, updateUserDto) - - accountService.findById(userid) - } - - override suspend fun followRequests( - loginUser: Long, - withIgnore: Boolean, - pageByMaxId: Page.PageByMaxId - ): PaginationList = transaction.transaction { - val request = - relationshipRepository.findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - loginUser, - true, - withIgnore, - pageByMaxId - ) - val actorIds = request.map { it.actorId } - - return@transaction PaginationList(accountService.findByIds(actorIds), request.next, request.prev) - } - - override suspend fun acceptFollowRequest(loginUser: Long, target: Long): Relationship = transaction.transaction { - relationshipService.acceptFollowRequest(loginUser, target) - - return@transaction fetchRelationship(loginUser, target) - } - - override suspend fun rejectFollowRequest(loginUser: Long, target: Long): Relationship = transaction.transaction { - relationshipService.rejectFollowRequest(loginUser, target) - - return@transaction fetchRelationship(loginUser, target) - } - - override suspend fun mute(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.mute(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun unmute(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.mute(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList { - val mutedAccounts = relationshipRepository.findByActorIdAndMuting(userid, true, pageByMaxId) - - return PaginationList( - accountService.findByIds(mutedAccounts.map { it.targetActorId }), - mutedAccounts.next, - mutedAccounts.prev - ) - } - - private fun from(account: Account): CredentialAccount { - return CredentialAccount( - id = account.id, - username = account.username, - acct = account.acct, - url = account.url, - displayName = account.displayName, - note = account.note, - avatar = account.avatar, - avatarStatic = account.avatarStatic, - header = account.header, - headerStatic = account.headerStatic, - locked = account.locked, - fields = account.fields, - emojis = account.emojis, - bot = account.bot, - group = account.group, - discoverable = account.discoverable, - createdAt = account.createdAt, - lastStatusAt = account.lastStatusAt, - statusesCount = account.statusesCount, - followersCount = account.followersCount, - noindex = account.noindex, - moved = account.moved, - suspendex = account.suspendex, - limited = account.limited, - followingCount = account.followingCount, - source = AccountSource( - account.note, - account.fields, - AccountSource.Privacy.public, - false, - 0 - ), - role = Role(0, "Admin", "", 32) - ) - } - - private suspend fun fetchRelationship(userid: Long, targetId: Long): Relationship { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(userid, targetId) - ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = userid, - targetActorId = targetId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userid) - ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = targetId, - targetActorId = userid, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - userService.updateUserStatistics(userid) - userService.updateUserStatistics(targetId) - - return Relationship( - id = targetId.toString(), - following = relationship.following, - showingReblogs = true, - notifying = false, - followedBy = inverseRelationship.following, - blocking = relationship.blocking, - blockedBy = inverseRelationship.blocking, - muting = relationship.muting, - mutingNotifications = relationship.muting, - requested = relationship.followRequest, - domainBlocking = false, - endorsed = false, - note = "" - ) - } - - private suspend fun isFollowing(userid: Long, target: Long): Boolean = - relationshipRepository.findByUserIdAndTargetUserId(userid, target)?.following ?: false - - companion object { - private val logger = LoggerFactory.getLogger(AccountApiServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt deleted file mode 100644 index e5a51f47..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.account - -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.mastodon.query.AccountQueryService -import org.springframework.stereotype.Service - -@Service -interface AccountService { - suspend fun findById(id: Long): Account - suspend fun findByIds(ids: List): List -} - -@Service -class AccountServiceImpl( - private val accountQueryService: AccountQueryService -) : AccountService { - override suspend fun findById(id: Long): Account = - accountQueryService.findById(id) ?: throw IllegalArgumentException("Account $id not found.") - - override suspend fun findByIds(ids: List): List = accountQueryService.findByIds(ids) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt deleted file mode 100644 index 3127d169..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.app - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator -import dev.usbharu.hideout.domain.mastodon.model.generated.Application -import dev.usbharu.hideout.domain.mastodon.model.generated.AppsRequest -import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.security.oauth2.core.AuthorizationGrantType -import org.springframework.security.oauth2.core.ClientAuthenticationMethod -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository -import org.springframework.security.oauth2.server.authorization.settings.ClientSettings -import org.springframework.security.oauth2.server.authorization.settings.TokenSettings -import org.springframework.stereotype.Service -import java.time.Duration -import java.util.* - -@Service -interface AppApiService { - suspend fun createApp(appsRequest: AppsRequest): Application -} - -@Service -class AppApiServiceImpl( - private val registeredClientRepository: RegisteredClientRepository, - private val secureTokenGenerator: SecureTokenGenerator, - private val passwordEncoder: PasswordEncoder, - private val transaction: Transaction -) : AppApiService { - override suspend fun createApp(appsRequest: AppsRequest): Application { - return transaction.transaction { - val id = UUID.randomUUID().toString() - val clientSecret = secureTokenGenerator.generate() - val registeredClient = RegisteredClient.withId(id) - .clientId(id) - .clientSecret(passwordEncoder.encode(clientSecret)) - .clientName(appsRequest.clientName) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - .redirectUri(appsRequest.redirectUris) - .tokenSettings( - TokenSettings.builder() - .accessTokenTimeToLive( - Duration.ofSeconds(31536000000) - ) - .build() - ) - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .scopes { it.addAll(parseScope(appsRequest.scopes.orEmpty())) } - .build() - registeredClientRepository.save(registeredClient) - - Application( - name = appsRequest.clientName, - vapidKey = "invalid-vapid-key", - website = appsRequest.website, - clientId = id, - clientSecret = clientSecret, - redirectUri = appsRequest.redirectUris - ) - } - } - - private fun parseScope(string: String): Set = string.split(" ").toSet() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt deleted file mode 100644 index 0f4c4157..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterAction.hide -import dev.usbharu.hideout.core.domain.model.filter.FilterAction.warn -import dev.usbharu.hideout.core.domain.model.filter.FilterMode -import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.filter.FilterType.* -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import dev.usbharu.hideout.core.query.model.FilterQueryService -import dev.usbharu.hideout.core.service.filter.MuteService -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequest.FilterAction -import dev.usbharu.hideout.domain.mastodon.model.generated.V1Filter.Context -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.runBlocking -import org.springframework.stereotype.Service - -@Suppress("TooManyFunctions") -interface MastodonFilterApiService { - fun v1Filters(userId: Long): Flow - - suspend fun deleteV1FilterById(userId: Long, id: Long) - - suspend fun getV1FilterById(userId: Long, id: Long): V1Filter? - - suspend fun createByV1Filter(userId: Long, v1FilterRequest: V1FilterPostRequest): V1Filter - - fun filterKeywords(userId: Long, filterId: Long): Flow - - suspend fun addKeyword(userId: Long, filterId: Long, keyword: FilterKeywordsPostRequest): FilterKeyword - - fun filterStatuses(userId: Long, filterId: Long): Flow - - suspend fun addFilterStatus(userId: Long, filterId: Long, filterStatusRequest: FilterStatusRequest): FilterStatus - - fun filters(userId: Long): Flow - - suspend fun deleteById(userId: Long, filterId: Long) - - suspend fun getById(userId: Long, filterId: Long): Filter? - - suspend fun deleteKeyword(userId: Long, keywordId: Long) - - suspend fun getKeywordById(userId: Long, keywordId: Long): FilterKeyword? - - suspend fun createFilter(userId: Long, filterPostRequest: FilterPostRequest): Filter - - suspend fun deleteFilterStatusById(userId: Long, filterPostsId: Long) - - suspend fun getFilterStatusById(userId: Long, filterPostsId: Long): FilterStatus? -} - -@Service -class MastodonFilterApiServiceImpl( - private val muteService: MuteService, - private val filterQueryService: FilterQueryService, - private val filterRepository: FilterRepository, - private val filterKeywordRepository: FilterKeywordRepository -) : MastodonFilterApiService { - override fun v1Filters(userId: Long): Flow { - return runBlocking { filterQueryService.findByUserId(userId) }.flatMap { filterQueryModel -> - filterQueryModel.keywords.map { - V1Filter( - id = it.id.toString(), - phrase = it.keyword, - context = filterQueryModel.context.map { filterType -> - when (filterType) { - home -> Context.home - notifications -> Context.notifications - public -> Context.public - thread -> Context.thread - account -> Context.account - } - }, - expiresAt = null, - irreversible = false, - wholeWord = (it.mode != FilterMode.WHOLE_WORD).not() - ) - } - }.asFlow() - } - - override suspend fun deleteV1FilterById(userId: Long, id: Long) { - val keywordId = filterQueryService.findByUserIdAndKeywordId(userId, id)?.keywords?.singleOrNull()?.id ?: return - - filterKeywordRepository.deleteById(keywordId) - } - - override suspend fun getV1FilterById(userId: Long, id: Long): V1Filter? { - val filterQueryModel = filterQueryService.findByUserIdAndKeywordId(userId, id) ?: return null - - val filterKeyword = filterQueryModel.keywords.firstOrNull() ?: return null - - return v1Filter(filterQueryModel, filterKeyword) - } - - private fun v1Filter( - filterQueryModel: FilterQueryModel, - filterKeyword: dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword - ) = V1Filter( - id = filterQueryModel.id.toString(), - phrase = filterKeyword.keyword, - context = filterQueryModel.context.map { - when (it) { - home -> Context.home - notifications -> Context.notifications - public -> Context.public - thread -> Context.thread - account -> Context.account - } - }, - expiresAt = null, - irreversible = false, - wholeWord = filterKeyword.mode == FilterMode.WHOLE_WORD - ) - - override suspend fun createByV1Filter(userId: Long, v1FilterRequest: V1FilterPostRequest): V1Filter { - val createFilter = muteService.createFilter( - title = v1FilterRequest.phrase, - context = v1FilterRequest.context.map { - when (it) { - V1FilterPostRequest.Context.home -> home - V1FilterPostRequest.Context.notifications -> notifications - V1FilterPostRequest.Context.public -> public - V1FilterPostRequest.Context.thread -> thread - V1FilterPostRequest.Context.account -> account - } - }, - action = warn, - keywords = listOf( - dev.usbharu.hideout.core.service.filter.FilterKeyword( - v1FilterRequest.phrase, - if (v1FilterRequest.wholeWord == true) { - FilterMode.WHOLE_WORD - } else { - FilterMode.NONE - } - ) - ), - loginUser = userId - ) - - return v1Filter(createFilter, createFilter.keywords.first()) - } - - override fun filterKeywords(userId: Long, filterId: Long): Flow = - runBlocking { filterQueryService.findByUserIdAndId(userId, filterId) } - ?.keywords - ?.map { - toFilterKeyword( - it - ) - } - .orEmpty() - .asFlow() - - override suspend fun addKeyword(userId: Long, filterId: Long, keyword: FilterKeywordsPostRequest): FilterKeyword { - val id = filterQueryService.findByUserIdAndId(userId, filterId)?.id - ?: throw IllegalArgumentException("filter not found.") - - val filterKeyword = filterKeywordRepository.save( - dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword( - id = filterKeywordRepository.generateId(), - filterId = id, - keyword = keyword.keyword, - mode = if (keyword.regex == true) { - FilterMode.REGEX - } else if (keyword.wholeWord == true) { - FilterMode.WHOLE_WORD - } else { - FilterMode.NONE - } - ) - ) - - return toFilterKeyword(filterKeyword) - } - - override fun filterStatuses(userId: Long, filterId: Long): Flow = emptyFlow() - - override suspend fun addFilterStatus( - userId: Long, - filterId: Long, - filterStatusRequest: FilterStatusRequest - ): FilterStatus { - TODO() - } - - override fun filters(userId: Long): Flow = - runBlocking { filterQueryService.findByUserId(userId) }.map { filterQueryModel -> - toFilter(filterQueryModel) - }.asFlow() - - private fun toFilter(filterQueryModel: FilterQueryModel) = Filter( - id = filterQueryModel.id.toString(), - title = filterQueryModel.name, - context = filterQueryModel.context.map { - when (it) { - home -> Filter.Context.home - notifications -> Filter.Context.notifications - public -> Filter.Context.public - thread -> Filter.Context.thread - account -> Filter.Context.account - } - }, - expiresAt = null, - filterAction = when (filterQueryModel.filterAction) { - warn -> Filter.FilterAction.warn - hide -> Filter.FilterAction.hide - }, - keywords = filterQueryModel.keywords.map { - toFilterKeyword(it) - }, - statuses = null - ) - - private fun toFilterKeyword(it: dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword) = FilterKeyword( - it.id.toString(), - it.keyword, - it.mode == FilterMode.WHOLE_WORD - ) - - override suspend fun deleteById(userId: Long, filterId: Long) = - filterRepository.deleteByUserIdAndId(userId, filterId) - - override suspend fun getById(userId: Long, filterId: Long): Filter? = - filterQueryService.findByUserIdAndId(userId, filterId)?.let { toFilter(it) } - - override suspend fun deleteKeyword(userId: Long, keywordId: Long) { - val id = filterQueryService.findByUserIdAndKeywordId(userId, keywordId)?.keywords?.singleOrNull()?.id ?: return - - filterKeywordRepository.deleteById(id) - } - - override suspend fun getKeywordById(userId: Long, keywordId: Long): FilterKeyword? { - return filterQueryService - .findByUserIdAndKeywordId(userId, keywordId) - ?.keywords - ?.firstOrNull() - ?.let { toFilterKeyword(it) } - } - - override suspend fun createFilter(userId: Long, filterPostRequest: FilterPostRequest): Filter { - val keywords = filterPostRequest.keywordsAttributes.orEmpty().map { - dev.usbharu.hideout.core.service.filter.FilterKeyword( - it.keyword, - if (it.regex == true) { - FilterMode.REGEX - } else if (it.wholeWord == true) { - FilterMode.WHOLE_WORD - } else { - FilterMode.NONE - } - ) - } - return toFilter( - muteService.createFilter( - title = filterPostRequest.title, - context = filterPostRequest.context.map { - when (it) { - FilterPostRequest.Context.home -> home - FilterPostRequest.Context.notifications -> notifications - FilterPostRequest.Context.public -> public - FilterPostRequest.Context.thread -> thread - FilterPostRequest.Context.account -> account - } - }, - action = when (filterPostRequest.filterAction) { - FilterAction.warn -> warn - FilterAction.hide -> warn - null -> warn - }, - keywords = keywords, - loginUser = userId - ) - ) - } - - override suspend fun deleteFilterStatusById(userId: Long, filterPostsId: Long) = Unit - - override suspend fun getFilterStatusById(userId: Long, filterPostsId: Long): FilterStatus? = null -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt deleted file mode 100644 index d24fe791..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.instance - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import org.springframework.stereotype.Service - -@Service -interface InstanceApiService { - suspend fun v1Instance(): V1Instance -} - -@Service -class InstanceApiServiceImpl(private val applicationConfig: ApplicationConfig) : InstanceApiService { - @Suppress("LongMethod") - override suspend fun v1Instance(): V1Instance { - val url = applicationConfig.url - return V1Instance( - uri = url.host, - title = "Hideout Server", - shortDescription = "Hideout test server", - description = "This server is operated for testing of Hideout." + - " We are not responsible for any events that occur when associating with this server", - email = "i@usbharu.dev", - version = "0.0.1", - urls = V1InstanceUrls("wss://${url.host}"), - stats = V1InstanceStats(1, 0, 0), - thumbnail = null, - languages = listOf("ja-JP"), - registrations = false, - approvalRequired = false, - invitesEnabled = false, - configuration = V1InstanceConfiguration( - accounts = V1InstanceConfigurationAccounts(1), - statuses = V1InstanceConfigurationStatuses( - 300, - 4, - 23 - ), - mediaAttachments = V1InstanceConfigurationMediaAttachments( - emptyList(), - 0, - 0, - 0, - 0 - ), - polls = V1InstanceConfigurationPolls( - 0, - 0, - 0, - 0 - ) - ), - contactAccount = Account( - id = "0", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = false, - createdAt = "0", - lastStatusAt = "0", - statusesCount = 1, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - rules = emptyList() - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt deleted file mode 100644 index 8e806d4d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.media - -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import org.springframework.stereotype.Service - -@Service -interface MediaApiService { - suspend fun postMedia(mediaRequest: MediaRequest): MediaAttachment -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt deleted file mode 100644 index 05f70e8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.media - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import org.springframework.stereotype.Service - -@Service -class MediaApiServiceImpl(private val mediaService: MediaService, private val transaction: Transaction) : - MediaApiService { - - override suspend fun postMedia(mediaRequest: MediaRequest): MediaAttachment { - return transaction.transaction { - val uploadLocalMedia = mediaService.uploadLocalMedia(mediaRequest) - val type = when (uploadLocalMedia.type) { - FileType.Image -> MediaAttachment.Type.image - FileType.Video -> MediaAttachment.Type.video - FileType.Audio -> MediaAttachment.Type.audio - FileType.Unknown -> MediaAttachment.Type.unknown - } - return@transaction MediaAttachment( - id = uploadLocalMedia.id.toString(), - type = type, - url = uploadLocalMedia.url, - previewUrl = uploadLocalMedia.thumbnailUrl, - description = mediaRequest.description, - blurhash = uploadLocalMedia.blurHash, - textUrl = uploadLocalMedia.url - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt deleted file mode 100644 index d632e04c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.notification - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.notification.Notification -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.service.notification.NotificationStore -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Component - -@Component -class MastodonNotificationStore(private val mastodonNotificationRepository: MastodonNotificationRepository) : - NotificationStore { - override suspend fun publishNotification( - notification: Notification, - user: Actor, - sourceActor: Actor?, - post: Post?, - reaction: Reaction? - ): Boolean { - val notificationType = when (notification.type) { - "mention" -> NotificationType.mention - "post" -> NotificationType.status - "repost" -> NotificationType.reblog - "follow" -> NotificationType.follow - "follow-request" -> NotificationType.follow_request - "reaction" -> NotificationType.favourite - else -> { - logger.debug("Notification type does not support. type: {}", notification.type) - return false - } - } - - if (notification.sourceActorId == null) { - logger.debug("Notification does not support. notification.sourceActorId is null") - return false - } - - val mastodonNotification = MastodonNotification( - id = notification.id, - userId = notification.userId, - type = notificationType, - createdAt = notification.createdAt, - accountId = notification.sourceActorId, - statusId = notification.postId, - reportId = null, - relationshipServeranceEvent = null - ) - - mastodonNotificationRepository.save(mastodonNotification) - - return true - } - - override suspend fun unpulishNotification(notificationId: Long): Boolean { - mastodonNotificationRepository.deleteById(notificationId) - return true - } - - companion object { - private val logger = LoggerFactory.getLogger(MastodonNotificationStore::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt deleted file mode 100644 index 32c762c6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.notification - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.NotificationType - -interface NotificationApiService { - - suspend fun notifications( - loginUser: Long, - types: List, - excludeTypes: List, - accountId: List, - page: Page - ): PaginationList - - suspend fun fingById(loginUser: Long, notificationId: Long): Notification? - - suspend fun clearAll(loginUser: Long) - - suspend fun dismiss(loginUser: Long, notificationId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt deleted file mode 100644 index 52caed8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.notification - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import dev.usbharu.hideout.mastodon.domain.model.NotificationType.* -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import dev.usbharu.hideout.mastodon.service.account.AccountService -import org.springframework.stereotype.Service - -@Service -class NotificationApiServiceImpl( - private val mastodonNotificationRepository: MastodonNotificationRepository, - private val transaction: Transaction, - private val accountService: AccountService, - private val statusQueryService: StatusQueryService -) : - NotificationApiService { - - override suspend fun notifications( - loginUser: Long, - types: List, - excludeTypes: List, - accountId: List, - page: Page - ): PaginationList = transaction.transaction { - val typesTmp = mutableListOf() - - typesTmp.addAll(types) - typesTmp.removeAll(excludeTypes) - - val mastodonNotifications = - mastodonNotificationRepository.findByUserIdAndInTypesAndInSourceActorId( - loginUser, - typesTmp, - accountId, - page - ) - - val accounts = accountService.findByIds( - mastodonNotifications.map { - it.accountId - } - ).associateBy { it.id.toLong() } - - val statuses = statusQueryService.findByPostIds(mastodonNotifications.mapNotNull { it.statusId }) - .associateBy { it.id.toLong() } - - val notifications = mastodonNotifications.map { - Notification( - id = it.id.toString(), - type = convertNotificationType(it.type), - createdAt = it.createdAt.toString(), - account = accounts.getValue(it.accountId), - status = statuses[it.statusId], - report = null, - relationshipSeveranceEvent = null - ) - } - - return@transaction PaginationList(notifications, mastodonNotifications.next, mastodonNotifications.prev) - } - - override suspend fun fingById(loginUser: Long, notificationId: Long): Notification? { - val findById = mastodonNotificationRepository.findById(notificationId) ?: return null - - if (findById.userId != loginUser) { - return null - } - - val account = accountService.findById(findById.accountId) - val status = findById.statusId?.let { statusQueryService.findByPostId(it) } - - return Notification( - id = findById.id.toString(), - type = convertNotificationType(findById.type), - createdAt = findById.createdAt.toString(), - account = account, - status = status, - report = null, - relationshipSeveranceEvent = null - ) - } - - override suspend fun clearAll(loginUser: Long) { - mastodonNotificationRepository.deleteByUserId(loginUser) - } - - override suspend fun dismiss(loginUser: Long, notificationId: Long) { - mastodonNotificationRepository.deleteByUserIdAndId(loginUser, notificationId) - } - - private fun convertNotificationType(notificationType: NotificationType): Notification.Type = - when (notificationType) { - mention -> Notification.Type.mention - status -> Notification.Type.status - reblog -> Notification.Type.reblog - follow -> Notification.Type.follow - follow_request -> Notification.Type.follow - favourite -> Notification.Type.follow_request - poll -> Notification.Type.poll - update -> Notification.Type.update - admin_sign_up -> Notification.Type.adminPeriodSign_up - admin_report -> Notification.Type.adminPeriodReport - severed_relationships -> Notification.Type.severed_relationships - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt deleted file mode 100644 index 07ae96c8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.status - -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.post.PostCreateDto -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.core.service.reaction.ReactionService -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.domain.mastodon.model.generated.Status.Visibility.* -import dev.usbharu.hideout.mastodon.domain.exception.StatusNotFoundException -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest -import dev.usbharu.hideout.mastodon.interfaces.api.status.toPostVisibility -import dev.usbharu.hideout.mastodon.interfaces.api.status.toStatusVisibility -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import dev.usbharu.hideout.mastodon.service.account.AccountService -import dev.usbharu.hideout.util.EmojiUtil -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -interface StatusesApiService { - suspend fun postStatus( - statusesRequest: StatusesRequest, - userId: Long, - ): Status - - suspend fun findById( - id: Long, - userId: Long?, - ): Status - - suspend fun emojiReactions( - postId: Long, - userId: Long, - emojiName: String, - ): Status - - suspend fun removeEmojiReactions( - postId: Long, - userId: Long, - emojiName: String, - ): Status -} - -@Service -@Suppress("LongParameterList") -class StatsesApiServiceImpl( - private val postService: PostService, - private val accountService: AccountService, - private val mediaRepository: MediaRepository, - private val transaction: Transaction, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val statusQueryService: StatusQueryService, - private val relationshipRepository: RelationshipRepository, - private val reactionService: ReactionService, - private val emojiService: EmojiService, -) : - StatusesApiService { - override suspend fun postStatus( - statusesRequest: StatusesRequest, - userId: Long, - ): Status = transaction.transaction { - logger.debug("START create post by mastodon api. {}", statusesRequest) - - val post = postService.createLocal( - PostCreateDto( - text = statusesRequest.status.orEmpty(), - overview = statusesRequest.spoiler_text, - visibility = statusesRequest.visibility.toPostVisibility(), - repolyId = statusesRequest.in_reply_to_id?.toLong(), - userId = userId, - mediaIds = statusesRequest.media_ids.map { it.toLong() } - ) - ) - val account = accountService.findById(userId) - - val replyUser = if (post.replyId != null) { - val findById = postRepository.findById(post.replyId) - if (findById == null) { - null - } else { - actorRepository.findById(findById.actorId)?.id - } - } else { - null - } - - // TODO: n+1解消 - val mediaAttachment = post.mediaIds.mapNotNull { mediaId -> - mediaRepository.findById(mediaId) - }.map { - it.toMediaAttachments() - } - - Status( - id = post.id.toString(), - uri = post.apId, - createdAt = Instant.ofEpochMilli(post.createdAt).toString(), - account = account, - content = post.text, - visibility = statusesRequest.visibility.toStatusVisibility(), - sensitive = post.sensitive, - spoilerText = post.overview.orEmpty(), - mediaAttachments = mediaAttachment, - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = post.url, - inReplyToId = post.replyId?.toString(), - inReplyToAccountId = replyUser?.toString(), - language = null, - text = post.text, - editedAt = null, - ) - } - - override suspend fun findById(id: Long, userId: Long?): Status = transaction.transaction { - val status = statusQueryService.findByPostId(id) ?: statusNotFound(id) - - return@transaction status(status, userId) - } - - private fun accessDenied(id: String): Nothing { - logger.debug("Access Denied $id") - throw StatusNotFoundException.ofId(id.toLong()) - } - - private fun statusNotFound(id: Long): Nothing { - logger.debug("Status Not Found $id") - throw StatusNotFoundException.ofId(id) - } - - private suspend fun status( - status: Status, - userId: Long?, - ): Status { - return when (status.visibility) { - public -> status - unlisted -> status - private -> { - if (userId == null) { - accessDenied(status.id) - } - - val relationship = - relationshipRepository.findByUserIdAndTargetUserId(userId, status.account.id.toLong()) - ?: accessDenied(status.id) - if (relationship.following) { - return status - } - accessDenied(status.id) - } - - direct -> accessDenied(status.id) - } - } - - override suspend fun emojiReactions(postId: Long, userId: Long, emojiName: String): Status { - status(statusQueryService.findByPostId(postId) ?: statusNotFound(postId), userId) - - val emoji = try { - if (EmojiUtil.isEmoji(emojiName)) { - UnicodeEmoji(emojiName) - } else { - emojiService.findByEmojiName(emojiName)!! - } - } catch (_: IllegalStateException) { - UnicodeEmoji("❤") - } catch (_: NullPointerException) { - UnicodeEmoji("❤") - } - reactionService.sendReaction(emoji, userId, postId) - return statusQueryService.findByPostId(postId) ?: statusNotFound(postId) - } - - override suspend fun removeEmojiReactions(postId: Long, userId: Long, emojiName: String): Status { - reactionService.removeReaction(userId, postId) - - return status(statusQueryService.findByPostId(postId) ?: statusNotFound(postId), userId) - } - - companion object { - private val logger = LoggerFactory.getLogger(StatusesApiService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt deleted file mode 100644 index 3bfd728d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.timeline - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.service.timeline.GenerateTimelineService -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import org.springframework.stereotype.Service - -@Suppress("LongParameterList") -interface TimelineApiService { - - suspend fun publicTimeline( - localOnly: Boolean = false, - remoteOnly: Boolean = false, - mediaOnly: Boolean = false, - page: Page - ): PaginationList - - suspend fun homeTimeline( - userId: Long, - page: Page - ): PaginationList -} - -@Service -class TimelineApiServiceImpl( - private val generateTimelineService: GenerateTimelineService, - private val transaction: Transaction -) : TimelineApiService { - - override suspend fun publicTimeline( - localOnly: Boolean, - remoteOnly: Boolean, - mediaOnly: Boolean, - page: Page - ): PaginationList = transaction.transaction { - return@transaction generateTimelineService.getTimeline(forUserId = 0, localOnly, mediaOnly, page) - } - - override suspend fun homeTimeline(userId: Long, page: Page): PaginationList = - transaction.transaction { - return@transaction generateTimelineService.getTimeline(forUserId = userId, page = page) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt deleted file mode 100644 index 36c8f322..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import dev.usbharu.hideout.core.domain.model.actor.Acct - -object AcctUtil { - fun parse(string: String): Acct { - if (string.isBlank()) { - throw IllegalArgumentException("Invalid acct.(Blank)") - } - return when (string.count { c -> c == '@' }) { - 0 -> { - Acct(string) - } - - 1 -> { - if (string.startsWith("@")) { - Acct(string.substring(1 until string.length)) - } else { - Acct(string.substringBefore("@"), string.substringAfter("@")) - } - } - - 2 -> { - if (string.startsWith("@")) { - val substring = string.substring(1 until string.length) - val userName = substring.substringBefore("@") - val domain = substring.substringAfter("@") - Acct( - userName, - domain.ifBlank { null } - ) - } else { - throw IllegalArgumentException("Invalid acct.(@ are in the wrong position)") - } - } - - else -> { - throw IllegalArgumentException("Invalid acct. (Too many @)") - } - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt index 9396a079..f211ffff 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt @@ -23,8 +23,6 @@ import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt index fbe5ffcf..ab8fee9d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.activitypub.service.activity.follow import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.service.follow.SendFollowDto import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.mockito.kotlin.eq diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt index 7c71a55a..bb96522a 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt @@ -16,7 +16,6 @@ package dev.usbharu.hideout.activitypub.service.common -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.resource.InMemoryCacheManager import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index 87bfbae4..33e745a8 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -32,11 +32,8 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.HtmlSanitizeConfig import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter -import dev.usbharu.hideout.core.service.post.PostService import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.plugins.* diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt index aab94666..c1daea13 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt @@ -18,12 +18,9 @@ package dev.usbharu.hideout.core.service.notification import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.notification.Notification import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt index 80cbb571..8d7c1676 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt @@ -21,11 +21,7 @@ import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteServi import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.HtmlSanitizeConfig import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.service.timeline.TimelineService import jakarta.validation.Validation import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt index e619f0f0..c02bfa10 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt @@ -20,7 +20,6 @@ package dev.usbharu.hideout.core.service.reaction import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.service.notification.NotificationService diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index 4af69e07..0d82e8b5 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -21,10 +21,7 @@ import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowServi import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.follow.SendFollowDto import dev.usbharu.hideout.core.service.notification.NotificationService import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt index e5806fc5..29db21c2 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt @@ -17,12 +17,9 @@ package dev.usbharu.hideout.core.service.timeline import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.timeline.Timeline import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository -import dev.usbharu.hideout.core.query.FollowerQueryService import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt index 6a574dc6..bfd88778 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt @@ -21,16 +21,11 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository import dev.usbharu.hideout.core.domain.model.instance.Instance -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.service.instance.InstanceService -import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.owl.producer.api.OwlProducer import jakarta.validation.Validation import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index de1477c6..e1676e7a 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -19,12 +19,7 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship import dev.usbharu.hideout.domain.mastodon.model.generated.Status diff --git a/hideout-core/src/test/kotlin/utils/PostBuilder.kt b/hideout-core/src/test/kotlin/utils/PostBuilder.kt index 4ddd2e89..41b9f746 100644 --- a/hideout-core/src/test/kotlin/utils/PostBuilder.kt +++ b/hideout-core/src/test/kotlin/utils/PostBuilder.kt @@ -19,7 +19,6 @@ package utils import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.HtmlSanitizeConfig import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter import jakarta.validation.Validation diff --git a/hideout-core/src/test/kotlin/utils/UserBuilder.kt b/hideout-core/src/test/kotlin/utils/UserBuilder.kt index 0aff8e44..fd929c81 100644 --- a/hideout-core/src/test/kotlin/utils/UserBuilder.kt +++ b/hideout-core/src/test/kotlin/utils/UserBuilder.kt @@ -19,7 +19,6 @@ package utils import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.Actor import jakarta.validation.Validation import kotlinx.coroutines.runBlocking import java.net.URL diff --git a/hideout-mastodon/build.gradle.kts b/hideout-mastodon/build.gradle.kts new file mode 100644 index 00000000..45be2756 --- /dev/null +++ b/hideout-mastodon/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") version "1.9.23" +} + +group = "dev.usbharu" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/hideout-mastodon/gradle/wrapper/gradle-wrapper.jar b/hideout-mastodon/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..249e5832 Binary files /dev/null and b/hideout-mastodon/gradle/wrapper/gradle-wrapper.jar differ diff --git a/hideout-mastodon/gradle/wrapper/gradle-wrapper.properties b/hideout-mastodon/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..7189c700 --- /dev/null +++ b/hideout-mastodon/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Jun 01 12:20:42 JST 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/hideout-mastodon/gradlew b/hideout-mastodon/gradlew new file mode 100644 index 00000000..1b6c7873 --- /dev/null +++ b/hideout-mastodon/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/hideout-mastodon/gradlew.bat b/hideout-mastodon/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/hideout-mastodon/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/hideout-mastodon/settings.gradle.kts b/hideout-mastodon/settings.gradle.kts new file mode 100644 index 00000000..c023f31a --- /dev/null +++ b/hideout-mastodon/settings.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" +} +rootProject.name = "hideout-mastodon" + diff --git a/hideout-mastodon/src/main/kotlin/Main.kt b/hideout-mastodon/src/main/kotlin/Main.kt new file mode 100644 index 00000000..27f6ee1a --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/Main.kt @@ -0,0 +1,5 @@ +package dev.usbharu + +fun main() { + println("Hello World!") +} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt index b557e259..7d939be0 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverAcceptTask import dev.usbharu.hideout.core.external.job.DeliverAcceptTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt index 7780d00d..9d880986 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverCreateTask import dev.usbharu.hideout.core.external.job.DeliverCreateTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt index 92961616..9ce3dad7 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverDeleteTask import dev.usbharu.hideout.core.external.job.DeliverDeleteTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt index 6408a73c..4867056a 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverReactionTask import dev.usbharu.hideout.core.external.job.DeliverReactionTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt index 7558197b..73179f07 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverRejectTask import dev.usbharu.hideout.core.external.job.DeliverRejectTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt index c811d2a6..949435ba 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverUndoTask import dev.usbharu.hideout.core.external.job.DeliverUndoTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt index 458f4f4c..7839bc01 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt @@ -19,10 +19,8 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.ReceiveFollowTask import dev.usbharu.hideout.core.external.job.ReceiveFollowTaskDef -import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.owl.consumer.AbstractTaskRunner import dev.usbharu.owl.consumer.TaskRequest import dev.usbharu.owl.consumer.TaskResult diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt index f25b8dce..9faec4c6 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt @@ -20,7 +20,6 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.UpdateActorTask import dev.usbharu.hideout.core.external.job.UpdateActorTaskDef -import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.owl.consumer.AbstractTaskRunner import dev.usbharu.owl.consumer.TaskRequest import dev.usbharu.owl.consumer.TaskResult