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.
|
||||
*/
|
||||
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import java.net.MalformedURLException
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
package mastodon.account
|
||||
|
||||
import dev.usbharu.hideout.SpringApplication
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedquery.FollowerQueryServiceImpl
|
||||
import dev.usbharu.owl.producer.api.OwlProducer
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
|
@ -18,7 +18,6 @@ package util
|
|||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||
import dev.usbharu.httpsignature.common.HttpMethod
|
||||
|
|
|
@ -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.StringOrObject
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService
|
||||
|
|
|
@ -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