mirror of https://github.com/usbharu/Hideout.git
wip
This commit is contained in:
parent
671d1e4f9e
commit
869ef1e111
|
@ -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)
|
||||||
|
}
|
Binary file not shown.
|
@ -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
|
|
@ -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" "$@"
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
||||||
|
plugins {
|
||||||
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
||||||
|
}
|
||||||
|
rootProject.name = "hideout-activitypub"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.usbharu
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
println("Hello World!")
|
||||||
|
}
|
|
@ -14,7 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
|
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
package mastodon.account
|
package mastodon.account
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
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 dev.usbharu.owl.producer.api.OwlProducer
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
|
@ -18,7 +18,6 @@ package util
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.application.external.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
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.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
||||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||||
import dev.usbharu.httpsignature.common.HttpMethod
|
import dev.usbharu.httpsignature.common.HttpMethod
|
||||||
|
|
|
@ -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",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = emptyList(),
|
|
||||||
@JsonProperty("object")
|
|
||||||
val apObject: String,
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
val published: String,
|
|
||||||
val to: List<String> = emptyList(),
|
|
||||||
val cc: List<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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<String> = emptyList(),
|
|
||||||
val cc: List<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
|
@ -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<String>,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<StringOrObject> = emptyList()
|
|
||||||
set(value) {
|
|
||||||
field = value.filterNot { it.isEmpty() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
constructor(context: List<StringOrObject?>?) {
|
|
||||||
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<String>() {
|
|
||||||
|
|
||||||
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<List<StringOrObject>>() {
|
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun isEmpty(value: List<StringOrObject>?): Boolean = value.isNullOrEmpty()
|
|
||||||
|
|
||||||
override fun isEmpty(provider: SerializerProvider?, value: List<StringOrObject>?): Boolean = value.isNullOrEmpty()
|
|
||||||
|
|
||||||
override fun serialize(value: List<StringOrObject>?, 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()}"
|
|
||||||
}
|
|
|
@ -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<String> = emptyList(),
|
|
||||||
override val actor: String,
|
|
||||||
override val id: String,
|
|
||||||
@JsonProperty("object") val apObject: String,
|
|
||||||
val content: String,
|
|
||||||
@JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List<Object> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = emptyList(),
|
|
||||||
override val id: String,
|
|
||||||
val attributedTo: String,
|
|
||||||
val content: String,
|
|
||||||
val published: String,
|
|
||||||
val to: List<String> = emptyList(),
|
|
||||||
val cc: List<String> = emptyList(),
|
|
||||||
val sensitive: Boolean = false,
|
|
||||||
val inReplyTo: String? = null,
|
|
||||||
val attachment: List<Document> = emptyList(),
|
|
||||||
@JsonDeserialize(contentUsing = ObjectDeserializer::class)
|
|
||||||
val tag: List<Object> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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<String, String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String, String>? = null
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
protected constructor()
|
|
||||||
|
|
||||||
constructor(string: String) : this() {
|
|
||||||
contextString = string
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(contextObject: Map<String, String>) : 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<StringOrObject>() {
|
|
||||||
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<String, String> = ctxt.readTreeAsValue(
|
|
||||||
readTree,
|
|
||||||
ctxt.typeFactory.constructType(object : TypeReference<Map<String, String>>() {})
|
|
||||||
)
|
|
||||||
StringOrObject(map)
|
|
||||||
} else {
|
|
||||||
StringOrObject("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StringORObjectSerializer : JsonSerializer<StringOrObject>() {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
|
@ -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<String> = 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()}"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Links>
|
|
||||||
) {
|
|
||||||
data class Links(
|
|
||||||
val rel: String,
|
|
||||||
val href: String
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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<String>,
|
|
||||||
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<String>,
|
|
||||||
val outbound: List<String>
|
|
||||||
)
|
|
||||||
|
|
||||||
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<String>,
|
|
||||||
val tosUrl: String,
|
|
||||||
val repositoryUrl: String,
|
|
||||||
val feedbackUrl: String,
|
|
||||||
) {
|
|
||||||
data class Maintainer(
|
|
||||||
val name: String,
|
|
||||||
val email: String
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> = emptyList()
|
|
||||||
set(value) {
|
|
||||||
field = value.filter { it.isNotBlank() }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected constructor()
|
|
||||||
constructor(type: List<String>) : 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<String>, type: String): List<String> {
|
|
||||||
val toMutableList = list.toMutableList()
|
|
||||||
toMutableList.add(type)
|
|
||||||
return toMutableList.distinct()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TypeSerializer : JsonSerializer<List<String>>() {
|
|
||||||
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
|
||||||
if (value?.size == 1) {
|
|
||||||
gen?.writeString(value[0])
|
|
||||||
} else {
|
|
||||||
gen?.writeStartArray()
|
|
||||||
value?.forEach {
|
|
||||||
gen?.writeString(it)
|
|
||||||
}
|
|
||||||
gen?.writeEndArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Object>() {
|
|
||||||
@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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String>, 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()}"
|
|
||||||
}
|
|
|
@ -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<Link>) {
|
|
||||||
data class Link(val rel: String, val type: String, val href: String)
|
|
||||||
}
|
|
|
@ -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<Post>
|
|
||||||
) : AnnounceQueryService {
|
|
||||||
override suspend fun findById(id: Long): Pair<Announce, Post>? {
|
|
||||||
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<Announce, Post>? {
|
|
||||||
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<String>, List<String>> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Post>) :
|
|
||||||
NoteQueryService {
|
|
||||||
override suspend fun findById(id: Long): Pair<Note, Post>? {
|
|
||||||
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<Note, Post>? {
|
|
||||||
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<dev.usbharu.hideout.core.domain.model.media.Media>): 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<String>, List<String>> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Person>
|
|
||||||
}
|
|
|
@ -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<Person> {
|
|
||||||
val person = try {
|
|
||||||
apUserService.getPersonByName(username)
|
|
||||||
} catch (_: UserNotFoundException) {
|
|
||||||
return ResponseEntity.notFound().build()
|
|
||||||
}
|
|
||||||
person.context += Constant.context
|
|
||||||
return ResponseEntity(person, HttpStatus.OK)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
"""<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
|
||||||
<Link rel="lrdd" type="application/xrd+xml"
|
|
||||||
template="${applicationConfig.url}/.well-known/webfinger?resource={uri}"/>
|
|
||||||
</XRD>"""
|
|
||||||
|
|
||||||
@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<String> = ResponseEntity(xml, HttpStatus.OK)
|
|
||||||
|
|
||||||
@GetMapping("/.well-known/host-meta.json", produces = ["application/json"])
|
|
||||||
fun hostmetJson(): ResponseEntity<String> = ResponseEntity(json, HttpStatus.OK)
|
|
||||||
}
|
|
|
@ -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<String>
|
|
||||||
}
|
|
|
@ -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<String> {
|
|
||||||
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<String>? {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Nodeinfo> {
|
|
||||||
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<Nodeinfo2_0> {
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Note>
|
|
||||||
}
|
|
|
@ -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<Note> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Unit>
|
|
||||||
}
|
|
|
@ -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<Unit> =
|
|
||||||
ResponseEntity(HttpStatus.NOT_IMPLEMENTED)
|
|
||||||
}
|
|
|
@ -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<WebFinger> = 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Announce, Post>?
|
|
||||||
suspend fun findByApId(apId: String): Pair<Announce, Post>?
|
|
||||||
}
|
|
|
@ -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<Note, Post>?
|
|
||||||
suspend fun findByApid(apId: String): Pair<Note, Post>?
|
|
||||||
}
|
|
|
@ -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<Accept>(transaction) {
|
|
||||||
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Accept>) {
|
|
||||||
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> = Accept::class.java
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Announce>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Announce>) {
|
|
||||||
apNoteService.fetchAnnounce(activity.activity)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isSupported(activityType: ActivityType): Boolean = ActivityType.Announce == activityType
|
|
||||||
|
|
||||||
override fun type(): Class<Announce> = Announce::class.java
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Create>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Create>) {
|
|
||||||
apNoteService.fetchNote(activity.activity.apObject as Note)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Create
|
|
||||||
|
|
||||||
override fun type(): Class<Create> = Create::class.java
|
|
||||||
}
|
|
|
@ -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<Delete>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Delete>) {
|
|
||||||
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> = Delete::class.java
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Follow>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Follow>) {
|
|
||||||
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> = Follow::class.java
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Like>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Like>) {
|
|
||||||
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> = Like::class.java
|
|
||||||
}
|
|
|
@ -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(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Reject>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Reject>) {
|
|
||||||
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> = Reject::class.java
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Undo>(transaction) {
|
|
||||||
override suspend fun internalProcess(activity: ActivityPubProcessContext<Undo>) {
|
|
||||||
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> = Undo::class.java
|
|
||||||
}
|
|
|
@ -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 <R : Object> apGet(url: String, signer: Actor? = null, responseClass: Class<R>): R
|
|
||||||
suspend fun <T : Object, R : Object> apPost(
|
|
||||||
url: String,
|
|
||||||
body: T? = null,
|
|
||||||
signer: Actor? = null,
|
|
||||||
responseClass: Class<R>
|
|
||||||
): R
|
|
||||||
|
|
||||||
suspend fun <T : Object> apPost(url: String, body: T? = null, signer: Actor? = null): String
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun <reified R : Object> APRequestService.apGet(url: String, signer: Actor? = null): R =
|
|
||||||
apGet(url, signer, R::class.java)
|
|
||||||
|
|
||||||
suspend inline fun <T : Object, reified R : Object> APRequestService.apPost(
|
|
||||||
url: String,
|
|
||||||
body: T? = null,
|
|
||||||
signer: Actor? = null
|
|
||||||
): R = apPost(url, body, signer, R::class.java)
|
|
|
@ -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 <R : Object> apGet(url: String, signer: Actor?, responseClass: Class<R>): 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 <T : Object, R : Object> apPost(
|
|
||||||
url: String,
|
|
||||||
body: T?,
|
|
||||||
signer: Actor?,
|
|
||||||
responseClass: Class<R>,
|
|
||||||
): R {
|
|
||||||
val bodyAsText = apPost(url, body, signer)
|
|
||||||
return objectMapper.readValue(bodyAsText, responseClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun <T : Object> 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 <T : Object> addContextIfNotNull(body: T?) = if (body != null) {
|
|
||||||
val context = mutableListOf<StringOrObject>()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 <T : Object> resolve(url: String, clazz: Class<T>, singer: Actor?): T
|
|
||||||
suspend fun <T : Object> resolve(url: String, clazz: Class<T>, singerId: Long?): T
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun <reified T : Object> APResourceResolveService.resolve(url: String, singer: Actor?): T =
|
|
||||||
resolve(url, T::class.java, singer)
|
|
||||||
|
|
||||||
suspend inline fun <reified T : Object> APResourceResolveService.resolve(url: String, singerId: Long?): T =
|
|
||||||
resolve(url, T::class.java, singerId)
|
|
|
@ -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 <T : Object> resolve(url: String, clazz: Class<T>, singerId: Long?): T =
|
|
||||||
internalResolve(url, singerId, clazz)
|
|
||||||
|
|
||||||
override suspend fun <T : Object> resolve(url: String, clazz: Class<T>, singer: Actor?): T =
|
|
||||||
internalResolve(url, singer, clazz)
|
|
||||||
|
|
||||||
private suspend fun <T : Object> internalResolve(url: String, singerId: Long?, clazz: Class<T>): T {
|
|
||||||
val key = genCacheKey(url, singerId)
|
|
||||||
|
|
||||||
cacheManager.putCache(key) {
|
|
||||||
runResolve(url, singerId?.let { actorRepository.findById(it) }, clazz)
|
|
||||||
}
|
|
||||||
return (cacheManager.getOrWait(key) as APResolveResponse<T>).objects
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun <T : Object> internalResolve(url: String, singer: Actor?, clazz: Class<T>): T {
|
|
||||||
val key = genCacheKey(url, singer?.id)
|
|
||||||
cacheManager.putCache(key) {
|
|
||||||
runResolve(url, singer, clazz)
|
|
||||||
}
|
|
||||||
return (cacheManager.getOrWait(key) as APResolveResponse<T>).objects
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun <T : Object> runResolve(url: String, singer: Actor?, clazz: Class<T>): 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<T : Object>(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<String, List<String>> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String, List<String>>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<String, List<String>>
|
|
||||||
) {
|
|
||||||
logger.debug("process activity: {}", type)
|
|
||||||
owlProducer.publishTask(
|
|
||||||
InboxTask(
|
|
||||||
json,
|
|
||||||
type,
|
|
||||||
httpRequest,
|
|
||||||
map
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<T : Object>(
|
|
||||||
private val transaction: Transaction,
|
|
||||||
private val allowUnauthorized: Boolean = false
|
|
||||||
) : ActivityPubProcessor<T> {
|
|
||||||
protected val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
|
||||||
|
|
||||||
override suspend fun process(activity: ActivityPubProcessContext<T>) {
|
|
||||||
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<T>)
|
|
||||||
}
|
|
|
@ -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<T : Object>(
|
|
||||||
val activity: T,
|
|
||||||
val jsonNode: JsonNode,
|
|
||||||
val httpRequest: HttpRequest,
|
|
||||||
val signature: Signature?,
|
|
||||||
val isAuthorized: Boolean
|
|
||||||
)
|
|
|
@ -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<T : Object> {
|
|
||||||
suspend fun process(activity: ActivityPubProcessContext<T>)
|
|
||||||
|
|
||||||
fun isSupported(activityType: ActivityType): Boolean
|
|
||||||
|
|
||||||
fun type(): Class<T>
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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<Emoji, CustomEmoji>
|
|
||||||
suspend fun fetchEmoji(emoji: Emoji): Pair<Emoji, CustomEmoji>
|
|
||||||
suspend fun findByEmojiName(emojiName: String): CustomEmoji?
|
|
||||||
}
|
|
|
@ -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<Emoji, CustomEmoji> {
|
|
||||||
val emoji = apResourceResolveServiceImpl.resolve<Emoji>(url, null as Long?)
|
|
||||||
return fetchEmoji(emoji)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun fetchEmoji(emoji: Emoji): Pair<Emoji, CustomEmoji> = 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Note, Post>
|
|
||||||
|
|
||||||
suspend fun fetchAnnounce(url: String, signerId: Long? = null): Pair<Announce, Post>
|
|
||||||
suspend fun fetchAnnounce(announce: Announce, signerId: Long? = null): Pair<Announce, Post>
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<Note, Post> {
|
|
||||||
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<Note>(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<Announce, Post> {
|
|
||||||
logger.debug("START Fetch Announce url: {}", url)
|
|
||||||
|
|
||||||
val post: Pair<Announce, Post>? = 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<Announce>(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<Announce, Post> {
|
|
||||||
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<Note, Post> = noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor)
|
|
||||||
|
|
||||||
private suspend fun saveNote(note: Note, targetActor: String?): Pair<Note, Post> {
|
|
||||||
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<Person, Actor>,
|
|
||||||
note: Note,
|
|
||||||
visibility: Visibility,
|
|
||||||
reply: Post?,
|
|
||||||
mediaList: List<Long>,
|
|
||||||
emojis: List<Long>
|
|
||||||
) = 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<Emoji>()
|
|
||||||
.map {
|
|
||||||
emojiService.fetchEmoji(it).second
|
|
||||||
}
|
|
||||||
.map {
|
|
||||||
it.id
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun visibility(
|
|
||||||
note: Note,
|
|
||||||
person: Pair<Person, Actor>
|
|
||||||
): 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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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?
|
|
||||||
}
|
|
|
@ -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, Post>
|
|
||||||
): 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Person, Actor>
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<Person, Actor> {
|
|
||||||
val userEntity = actorRepository.findByUrl(url)
|
|
||||||
|
|
||||||
if (userEntity != null && idOverride == null) {
|
|
||||||
return entityToPerson(userEntity, userEntity.url) to userEntity
|
|
||||||
}
|
|
||||||
|
|
||||||
val person = apResourceResolveService.resolve<Person>(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
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.StringORObjectSerializer
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
|
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
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.HttpSignatureFilter
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService
|
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService
|
||||||
|
|
|
@ -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"),
|
||||||
|
}
|
|
@ -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)
|
|
|
@ -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<Long> = 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<Long> = 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" +
|
|
||||||
")"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Actor>
|
|
||||||
|
|
||||||
suspend fun findByName(name: String): List<Actor>
|
|
||||||
|
|
||||||
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<Long>): List<Actor>
|
|
||||||
|
|
||||||
suspend fun findByKeyId(keyId: String): Actor?
|
|
||||||
|
|
||||||
suspend fun delete(id: Long)
|
|
||||||
|
|
||||||
suspend fun nextId(): Long
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue