Compare commits
242 Commits
activity-p
...
main
Author | SHA1 | Date |
---|---|---|
Natty | 5363a0c137 | |
Natty | 5666bb4622 | |
Natty | 8aa2a4dac4 | |
Natty | ff6458ea2e | |
Natty | ad253fb02b | |
Natty | 11ff0ad2c0 | |
Natty | 0a23953ae9 | |
Natty | 2acc41587a | |
Natty | 56f80f290a | |
Natty | d598387795 | |
Natty | 52cced9537 | |
Natty | c10594bf7e | |
Natty | 369df105c9 | |
Natty | e53afb3313 | |
Natty | ad3b0e1ffb | |
Natty | 51375e2ded | |
Natty | 531b10ef85 | |
Natty | 845fcb385b | |
Natty | e4beba0aea | |
Natty | 161996632b | |
Natty | 5c568b1657 | |
Natty | 5fb85e0db6 | |
Natty | 80c5bf8ae6 | |
Natty | b9160305f1 | |
Natty | 9c42b20fa9 | |
Natty | 69c126d860 | |
Natty | 7581ecf331 | |
Natty | 766fd8ea7d | |
Natty | 5241b18b0d | |
Natty | 88df8eca55 | |
Natty | 62fc36ff03 | |
Natty | 611afc591c | |
Natty | 09f564746f | |
Natty | 607cb2ded2 | |
Natty | aebc9803db | |
Natty | cb9bf082f4 | |
Natty | 87e9fb36e1 | |
Natty | 1173360265 | |
Natty | 78c93f3c20 | |
Natty | b74f2d69a4 | |
Natty | e06906dd6e | |
Natty | 155e458806 | |
Natty | ea4751400b | |
Natty | 7c44e55597 | |
Natty | 6c7e3d79e0 | |
Natty | bca16253af | |
Natty | 1f10156ebb | |
Natty | aeb94687b5 | |
Natty | 553dbb9b7b | |
Natty | e21c5a8132 | |
Natty | 8b6bdd6e02 | |
Natty | b5812de552 | |
Natty | 4f4a871ae5 | |
Natty | e1fd078367 | |
Natty | ce46782318 | |
Natty | aefef079a7 | |
Natty | 156ad5c499 | |
Natty | 13e5d447e8 | |
Natty | db444adcc1 | |
Natty | ff3f0927fb | |
Natty | d12b65fff7 | |
Natty | 812d4ee6b2 | |
Natty | 8618b2084d | |
Natty | 014b394f66 | |
Natty | 0dff0c0785 | |
Natty | ffed556107 | |
Natty | 5aceca72bd | |
Natty | a640890cad | |
Natty | 799730bedc | |
Natty | b295c66f0c | |
Natty | 66ce0de3fa | |
Natty | 236502ad05 | |
Natty | cbf6c7fe43 | |
Natty | ea0d681e5c | |
Natty | dc86b4e67d | |
Natty | 7f0be08799 | |
Natty | 1394557f81 | |
Natty | 459f944243 | |
Natty | a169ebb004 | |
Natty | 7c2909bceb | |
Natty | 8d9415b3ab | |
Natty | d64d34b2ef | |
Natty | 169e85dead | |
Natty | 16739635d1 | |
Natty | 7bfb5c0f91 | |
Natty | c8d658f807 | |
Natty | 5430fc81f4 | |
Natty | f441de806f | |
Natty | 7b02f84271 | |
Natty | ad3528055f | |
Natty | 98fb2ef0d8 | |
Natty | 94cff7c2c8 | |
Natty | 2e4903e603 | |
Natty | b9f0f33b3c | |
Natty | 185262eb05 | |
Natty | 475c33e57e | |
Natty | 3c16574d03 | |
Natty | a51e412cb8 | |
Natty | ff00dfebb6 | |
Natty | 364074aecc | |
Natty | d16f9d704d | |
Natty | ce1bfc0ee1 | |
Natty | 4dd3cd59ac | |
Natty | 13f5ad3672 | |
Natty | 789852211b | |
Natty | fdf9966ef6 | |
Natty | 4834daceec | |
Natty | 80a29f771f | |
Natty | 2fea3b79a7 | |
Natty | 22208d4c68 | |
Natty | b2a55f1d6d | |
Natty | 1366adcebf | |
Natty | f06c74c85c | |
Natty | b2543b29d3 | |
Natty | 1bef42ead5 | |
Natty | a658452138 | |
Natty | 3b1eab8069 | |
Natty | 811fecf99a | |
Natty | c6d84d6287 | |
Natty | 024cdef68c | |
Natty | deb7f6ef5e | |
Natty | 074c6f999e | |
Natty | c2cfd7e007 | |
Natty | 9f633d0749 | |
Natty | bbc4f84ceb | |
Natty | 152c4e6fc6 | |
Natty | 93caa314c9 | |
Natty | 15b0ea8e26 | |
Natty | 42bf912bb9 | |
Natty | ab18633fbc | |
Natty | 6003f5e404 | |
Natty | 6b96f60b43 | |
Natty | ce8cb5aad0 | |
Natty | 9c8489ced7 | |
Natty | 64350cfcb8 | |
Natty | b50249da02 | |
Natty | 7fbcc4ec19 | |
Natty | 05469f68a8 | |
Natty | 69cb1c5220 | |
Natty | 76c4d0267f | |
Natty | 7ad46191ec | |
Natty | bf2d475f78 | |
Natty | 72ed3391fc | |
Natty | 10bddd886e | |
Natty | c3e7791b3e | |
Natty | f23aae4ac7 | |
Natty | cb8e42d219 | |
Natty | f55ff2b761 | |
Natty | 73fa4d2884 | |
Natty | e2cab2aa9b | |
Natty | 79660fb816 | |
Natty | 81d0c678d8 | |
Natty | d1ca62a807 | |
Natty | 10256c683c | |
Natty | 7eb03d59fb | |
Natty | 4f6e6163cc | |
Natty | e1859c98bd | |
Natty | 3b8997d153 | |
Natty | 2173ee452f | |
Natty | 0ed23fe68e | |
Natty | 0bb0c775dc | |
Natty | 4cb7431681 | |
Natty | e53e274d1d | |
Natty | cb645ef0f6 | |
Natty | b4611bda78 | |
Natty | 0ed06ee68f | |
Natty | 3efc376f2b | |
Natty | 7eed5d6b62 | |
Natty | b801ab83ac | |
Natty | fdfd3163b1 | |
Natty | e7812a816b | |
Natty | 0ad015cd68 | |
Natty | 6908a2f350 | |
Natty | a5ab2acca0 | |
Natty | c8627a996c | |
Natty | 2c3d675392 | |
Natty | 5a8dc04915 | |
Natty | a34829c63d | |
Natty | cf04146d2f | |
Natty | 5b9b813037 | |
Natty | 970644ffc7 | |
Natty | 8e02e46be5 | |
Natty | f4fa5925f7 | |
Natty | 734ace5d05 | |
Natty | 97407097b7 | |
Natty | 7dc38ada9a | |
Natty | acdc3e8bc1 | |
Natty | 0755dac002 | |
Natty | 4bbc368f92 | |
Natty | 3cd43d840a | |
Natty | 771795d81f | |
Natty | f0e56deca9 | |
Natty | 5572695515 | |
Natty | 18d526cf8c | |
Natty | fc86f0e29c | |
Natty | 42e68fffcd | |
Natty | f34be3a104 | |
Natty | 216f4229fc | |
Natty | c6a282c26e | |
Natty | 91883c6b36 | |
Natty | 6ed6066b1f | |
Natty | 146c072c92 | |
Natty | c4a8ebebf3 | |
Natty | 42fa83c6e2 | |
Natty | 86d5c87e9a | |
Natty | 23a63f2fe9 | |
Natty | 68abd4f787 | |
Natty | 8ee5ee8bc0 | |
Natty | 03be3d02e3 | |
Natty | 38753bab28 | |
Natty | d0d977e6eb | |
Natty | 26bd6fe4b2 | |
Natty | c4fd99fa45 | |
Natty | d2bc679740 | |
Natty | 154cc27c07 | |
Natty | 95bce443be | |
Natty | 703e1191c2 | |
Natty | 453891ddf4 | |
Natty | c45ec852dd | |
Natty | 4431a3ad62 | |
Natty | a6ee6bfbde | |
Natty | 7c8e65f556 | |
Natty | 8009546bfe | |
Natty | 52dc491a47 | |
Natty | 9b26691ff4 | |
Natty | 24d44632e0 | |
Natty | 46e0766a36 | |
Natty | 1af8f4e213 | |
Natty | 95141388fa | |
Natty | a8636947b9 | |
Natty | be08be61c6 | |
Natty | c58bc2994d | |
Natty | fc0ec19afb | |
Natty | a0bdab23e7 | |
Natty | b4e01fee63 | |
Natty | 36c3507d84 | |
Natty | 2525b3a50a | |
Natty | 3837980e50 | |
Natty | 5c8db9b243 | |
Natty | 98c5bcb4c4 | |
Natty | df275de905 | |
Natty | dde74dd56b |
|
@ -1,2 +1,2 @@
|
|||
[registries.crates-io]
|
||||
protocol = "sparse"
|
||||
protocol = "sparse"
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
nattyarch.local {
|
||||
magnetar-dev.local {
|
||||
log {
|
||||
|
||||
}
|
||||
|
||||
handle /.well-known/host-meta {
|
||||
reverse_proxy 127.0.0.1:4939
|
||||
}
|
||||
|
||||
handle /.well-known/webfinger {
|
||||
reverse_proxy 127.0.0.1:4939
|
||||
|
@ -20,7 +24,7 @@ nattyarch.local {
|
|||
}
|
||||
|
||||
@render_html {
|
||||
not path /api* /proxy* /files* /avatar* /identicon* /streaming
|
||||
not path /api* /proxy* /files* /avatar* /identicon*
|
||||
header Accept text/html*
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pipeline:
|
||||
steps:
|
||||
publish-docker-latest:
|
||||
image: docker.io/plugins/kaniko
|
||||
settings:
|
||||
|
@ -12,7 +12,5 @@ pipeline:
|
|||
from_secret: docker_password
|
||||
|
||||
when:
|
||||
event: [push]
|
||||
branch: [main]
|
||||
|
||||
branches: [main]
|
||||
- event: push
|
||||
branch: main
|
||||
|
|
File diff suppressed because it is too large
Load Diff
105
Cargo.toml
105
Cargo.toml
|
@ -8,101 +8,142 @@ license = "AGPL-3.0-only"
|
|||
[workspace]
|
||||
members = [
|
||||
".",
|
||||
"ext_activity_pub",
|
||||
"ext_activity_streams",
|
||||
"ext_federation",
|
||||
"ext_nodeinfo",
|
||||
"ext_webfinger",
|
||||
"ext_calckey_model",
|
||||
"ext_model",
|
||||
"fe_calckey",
|
||||
"magnetar_common",
|
||||
"magnetar_runtime",
|
||||
"magnetar_sdk",
|
||||
"core"
|
||||
"magnetar_mmm_parser",
|
||||
"core",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.0"
|
||||
version = "0.3.0-alpha"
|
||||
edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1"
|
||||
axum = "0.6"
|
||||
cached = "0.46"
|
||||
async-stream = "0.3"
|
||||
axum = "0.7"
|
||||
axum-extra = "0.9"
|
||||
base64 = "0.22"
|
||||
bytes = "1.7"
|
||||
cached = "0.54"
|
||||
cfg-if = "1"
|
||||
chrono = "0.4"
|
||||
compact_str = "0.8"
|
||||
dotenvy = "0.15"
|
||||
ed25519-dalek = "2.1"
|
||||
either = "1.9"
|
||||
emojis = "0.6"
|
||||
futures = "0.3"
|
||||
futures-channel = "0.3"
|
||||
futures-core = "0.3"
|
||||
futures-util = "0.3"
|
||||
headers = "0.3"
|
||||
http = "0.2"
|
||||
hyper = "0.14"
|
||||
js-sys = "0.3"
|
||||
log = "0.4"
|
||||
miette = "5.9"
|
||||
headers = "0.4"
|
||||
http = "1.0"
|
||||
httpdate = "1"
|
||||
hyper = "1.1"
|
||||
idna = "1"
|
||||
indexmap = "2.2"
|
||||
itertools = "0.13"
|
||||
kdl = "4"
|
||||
lru = "0.12"
|
||||
miette = "7"
|
||||
nom = "7"
|
||||
nom_locate = "4"
|
||||
percent-encoding = "2.2"
|
||||
redis = "0.23"
|
||||
reqwest = "0.11"
|
||||
sea-orm = "0.12"
|
||||
sea-orm-migration = "0.12"
|
||||
priority-queue = "2.0"
|
||||
quick-xml = "0.36"
|
||||
redis = "0.26"
|
||||
regex = "1.9"
|
||||
rmp-serde = "1.3"
|
||||
rsa = "0.9"
|
||||
reqwest = "0.12"
|
||||
sea-orm = "1"
|
||||
sea-orm-migration = "1"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
strum = "0.25"
|
||||
serde_urlencoded = "0.7"
|
||||
sha2 = "0.10"
|
||||
smallvec = "1.13"
|
||||
strum = "0.26"
|
||||
tera = { version = "1", default-features = false }
|
||||
thiserror = "1"
|
||||
thiserror = "2"
|
||||
tokio = "1.24"
|
||||
tokio-util = "0.7"
|
||||
tokio-stream = "0.1"
|
||||
toml = "0.8"
|
||||
tower = "0.4"
|
||||
tower-http = "0.4"
|
||||
tower = "0.5"
|
||||
tower-http = "0.6"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
ts-rs = "7"
|
||||
ulid = "1"
|
||||
unicode-segmentation = "1.10"
|
||||
url = "2.3"
|
||||
walkdir = "2.3"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = "0.3"
|
||||
|
||||
[dependencies]
|
||||
magnetar_activity_pub = { path = "./ext_activity_pub" }
|
||||
magnetar_core = { path = "./core" }
|
||||
magnetar_common = { path = "./magnetar_common" }
|
||||
magnetar_federation = { path = "./ext_federation" }
|
||||
magnetar_host_meta = { path = "./ext_host_meta" }
|
||||
magnetar_webfinger = { path = "./ext_webfinger" }
|
||||
magnetar_nodeinfo = { path = "./ext_nodeinfo" }
|
||||
magnetar_calckey_model = { path = "./ext_calckey_model" }
|
||||
magnetar_model = { path = "./ext_model" }
|
||||
magnetar_runtime = { path = "./magnetar_runtime" }
|
||||
magnetar_sdk = { path = "./magnetar_sdk" }
|
||||
|
||||
cached = { workspace = true }
|
||||
lru = { workspace = true }
|
||||
|
||||
chrono = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
|
||||
async-trait = { workspace = true }
|
||||
axum = { workspace = true, features = ["macros", "headers"] }
|
||||
futures-util = { workspace = true }
|
||||
axum = { workspace = true, features = ["macros"] }
|
||||
axum-extra = { workspace = true, features = ["typed-header"] }
|
||||
async-stream = { workspace = true }
|
||||
headers = { workspace = true }
|
||||
hyper = { workspace = true, features = ["full"] }
|
||||
reqwest = { workspace = true, features = ["hickory-dns"] }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-stream = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
tower-http = { workspace = true, features = ["cors", "trace", "fs"] }
|
||||
ulid = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
cfg-if = { workspace = true }
|
||||
|
||||
bytes = { workspace = true }
|
||||
compact_str = { workspace = true }
|
||||
either = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
miette = { workspace = true, features = ["fancy"] }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
|
||||
percent-encoding = { workspace = true }
|
||||
|
||||
kdl = { workspace = true }
|
||||
rmp-serde = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_urlencoded = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] }
|
||||
|
||||
unicode-segmentation = { workspace = true }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
lto = true
|
||||
|
|
52
Dockerfile
52
Dockerfile
|
@ -1,20 +1,25 @@
|
|||
FROM docker.io/alpine:3.18 as build_fe
|
||||
|
||||
RUN apk add --no-cache --no-progress git alpine-sdk nodejs-current npm
|
||||
|
||||
WORKDIR /fe_calckey
|
||||
COPY ./fe_calckey/frontend ./frontend
|
||||
WORKDIR /fe_calckey/frontend
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm i --frozen-lockfile
|
||||
|
||||
RUN env NODE_ENV=production sh -c "pnpm run build && pnpm run gulp"
|
||||
|
||||
|
||||
FROM docker.io/rust:1.71-bullseye as build
|
||||
FROM docker.io/rust:1.82-bookworm as build
|
||||
|
||||
RUN update-ca-certificates
|
||||
|
||||
RUN apt update
|
||||
RUN apt install -y git nodejs npm
|
||||
RUN npm install -g corepack
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
WORKDIR /magnetar
|
||||
COPY ./ .
|
||||
|
||||
WORKDIR /magnetar/fe_calckey/frontend
|
||||
RUN pnpm i --frozen-lockfile
|
||||
|
||||
WORKDIR /magnetar
|
||||
RUN cargo build --release --locked --workspace --bins
|
||||
|
||||
WORKDIR /magnetar/fe_calckey/frontend
|
||||
RUN env NODE_ENV=production sh -c "pnpm run build && pnpm run gulp"
|
||||
|
||||
ENV USER=magnetar
|
||||
ENV UID=10001
|
||||
|
||||
|
@ -26,13 +31,10 @@ RUN adduser \
|
|||
--uid "${UID}" \
|
||||
"${USER}"
|
||||
|
||||
WORKDIR /magnetar
|
||||
FROM docker.io/debian:bookworm-slim
|
||||
|
||||
COPY ./ .
|
||||
|
||||
RUN cargo build --release --locked --workspace --bins
|
||||
|
||||
FROM docker.io/debian:bullseye-slim
|
||||
RUN apt update
|
||||
RUN apt install -y openssl
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
|
@ -42,10 +44,10 @@ COPY --from=build /etc/group /etc/group
|
|||
WORKDIR /magnetar
|
||||
|
||||
WORKDIR /magnetar/fe_calckey/frontend
|
||||
COPY --from=build_fe /fe_calckey/frontend/built ./built
|
||||
COPY --from=build_fe /fe_calckey/frontend/assets ./assets
|
||||
COPY --from=build_fe /fe_calckey/frontend/client/assets ./client/assets
|
||||
COPY --from=build_fe /fe_calckey/frontend/assets-be ./assets-be
|
||||
COPY --from=build /magnetar/fe_calckey/frontend/built ./built
|
||||
COPY --from=build /magnetar/fe_calckey/frontend/assets ./assets
|
||||
COPY --from=build /magnetar/fe_calckey/frontend/client/assets ./client/assets
|
||||
COPY --from=build /magnetar/fe_calckey/frontend/assets-be ./assets-be
|
||||
|
||||
WORKDIR /magnetar
|
||||
|
||||
|
@ -59,4 +61,4 @@ USER magnetar:magnetar
|
|||
EXPOSE 4938/tcp
|
||||
EXPOSE 4939/tcp
|
||||
|
||||
ENTRYPOINT ["/magnetar/magnetar"]
|
||||
ENTRYPOINT ["/magnetar/magnetar"]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
*
|
||||
!.gitignore
|
||||
!default.toml
|
||||
!default.toml
|
||||
!default-vars.kdl
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
cache {
|
||||
local-user-cache {
|
||||
// Size is unlimited
|
||||
lifetime 300min
|
||||
}
|
||||
|
||||
emoji-cache {
|
||||
size 4096
|
||||
}
|
||||
|
||||
remote-instance-cache {
|
||||
size 256
|
||||
lifetime 100s
|
||||
}
|
||||
|
||||
drive-file-cache {
|
||||
size 128
|
||||
lifetime 10s
|
||||
}
|
||||
}
|
||||
|
||||
api-model {
|
||||
note {
|
||||
buffer 10
|
||||
}
|
||||
|
||||
notification {
|
||||
buffer 10
|
||||
}
|
||||
}
|
||||
|
||||
activity-pub {
|
||||
user-agent "magnetar/$version ($host)"
|
||||
}
|
|
@ -47,6 +47,33 @@
|
|||
# Environment variable: MAG_C_BIND_ADDR
|
||||
# networking.bind_addr = "::"
|
||||
|
||||
# [Optional]
|
||||
# The URL of a media proxy
|
||||
# Default: null
|
||||
# Environment variable: MAG_C_MEDIA_PROXY
|
||||
# networking.media_proxy = ""
|
||||
|
||||
# [Optional]
|
||||
# Whether to proxy remote files through this instance
|
||||
# Default: false
|
||||
# Environment variable: MAG_C_PROXY_REMOTE_FILES
|
||||
# networking.proxy_remote_files = false
|
||||
|
||||
# ------------------------------[ RPC CONNECTION ]-----------------------------
|
||||
|
||||
# [Optional]
|
||||
# A type of connection to use for the application's internal RPC
|
||||
# Possible values: "none", "tcp", "unix"
|
||||
# Default: "none"
|
||||
# Environment variable: MAG_C_RPC_CONNECTION_TYPE
|
||||
# rpc.connection_type = "none"
|
||||
|
||||
# [Optional]
|
||||
# The corresponding bind address (or path for Unix-domain sockets) for the internal RPC
|
||||
# Default: ""
|
||||
# Environment variable: MAG_C_RPC_BIND_ADDR
|
||||
# rpc.bind_addr = ""
|
||||
|
||||
# -----------------------------[ CALCKEY FRONTEND ]----------------------------
|
||||
|
||||
# [Optional]
|
||||
|
@ -70,7 +97,6 @@
|
|||
# -------------------------------[ FEDERATION ]--------------------------------
|
||||
|
||||
|
||||
|
||||
# --------------------------------[ BRANDING ]---------------------------------
|
||||
|
||||
# [Optional]
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
fmt::Display,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Acct(String);
|
||||
|
@ -23,6 +26,12 @@ impl AsRef<str> for Acct {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for Acct {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "acct:{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Acct {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
|
|
@ -8,10 +8,12 @@ pub mod acct;
|
|||
|
||||
pub trait ContentType: Serialize {
|
||||
fn mime_type(&self) -> &'static str;
|
||||
|
||||
fn alt_mime_types(&self) -> &[&'static str];
|
||||
}
|
||||
|
||||
macro_rules! content_type {
|
||||
($visib:vis $typ:ident, $expression:expr) => {
|
||||
($visib:vis $typ:ident, $expression:expr $(,$alt:expr)*) => {
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||
$visib struct $typ;
|
||||
|
||||
|
@ -31,13 +33,17 @@ macro_rules! content_type {
|
|||
fn mime_type(&self) -> &'static str {
|
||||
$expression
|
||||
}
|
||||
|
||||
fn alt_mime_types(&self) -> &[&'static str] {
|
||||
&[$($alt),*]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for $typ {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let content_type = String::deserialize(deserializer)?;
|
||||
|
||||
if matches!(content_type.as_ref(), $expression) {
|
||||
if matches!(content_type.as_ref(), $expression $(| $alt)*) {
|
||||
Ok(Self)
|
||||
} else {
|
||||
Err(Error::custom(format!(
|
||||
|
@ -54,9 +60,11 @@ pub mod content_type {
|
|||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
content_type!(pub ContentActivityStreams, "application/activity+json");
|
||||
content_type!(pub ContentActivityStreams, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "application/activity+json", "application/activity+json; charset=utf-8");
|
||||
content_type!(pub ContentActivityJson, "application/activity+json", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "application/activity+json; charset=utf-8");
|
||||
content_type!(pub ContentHtml, "text/html");
|
||||
content_type!(pub ContentJson, "application/json");
|
||||
content_type!(pub ContentXrdXml, "application/xrd+xml");
|
||||
content_type!(pub ContentJrdJson, "application/jrd+json");
|
||||
content_type!(pub ContentMultipartFormData, "multipart/form-data");
|
||||
content_type!(pub ContentUrlEncoded, "application/x-www-form-urlencoded");
|
||||
|
@ -112,6 +120,9 @@ pub mod rel {
|
|||
|
||||
link_rel!(pub RelWebFingerProfilePage, "http://webfinger.net/rel/profile-page");
|
||||
link_rel!(pub RelSelf, "self");
|
||||
link_rel!(pub RelNext, "next");
|
||||
link_rel!(pub RelPrev, "prev");
|
||||
link_rel!(pub RelLrdd, "lrdd");
|
||||
link_rel!(pub RelOStatusSubscribe, "http://ostatus.org/schema/1.0/subscribe");
|
||||
link_rel!(pub RelNodeInfo20, "http://nodeinfo.diaspora.software/ns/schema/2.0");
|
||||
link_rel!(pub RelNodeInfo21, "http://nodeinfo.diaspora.software/ns/schema/2.1");
|
||||
|
@ -139,8 +150,7 @@ where
|
|||
let dt = data
|
||||
.iter()
|
||||
.filter_map(Value::as_str)
|
||||
.filter_map(|val| T::from_str(val).ok())
|
||||
.next();
|
||||
.find_map(|val| T::from_str(val).ok());
|
||||
|
||||
if let Some(value) = dt {
|
||||
Ok(ListContaining(value))
|
||||
|
|
|
@ -1,237 +0,0 @@
|
|||
pub mod link;
|
||||
pub mod object;
|
||||
pub mod recipe;
|
||||
|
||||
use crate::link::Link;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use url::Url;
|
||||
|
||||
pub trait ObjectSingle: Clone + Debug + Sized + 'static {
|
||||
fn get_type() -> &'static str;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectRaw<T> {
|
||||
#[serde(flatten)]
|
||||
data: Box<T>,
|
||||
#[serde(flatten)]
|
||||
raw: Value,
|
||||
}
|
||||
|
||||
impl<T> Deref for ObjectRaw<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ObjectRaw<T> {
|
||||
pub fn into_inner(self) -> Box<T> {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for ObjectRaw<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
self.as_data()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ObjectRaw<T> {
|
||||
pub fn as_data(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub fn as_json(&self) -> &Value {
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Resolver: Send + Sync {
|
||||
type Error: Send + Sync;
|
||||
|
||||
async fn resolve<T: for<'a> Deserialize<'a>>(&self, id: &str) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Resolvable {
|
||||
type Output: Clone + Send + Sync;
|
||||
|
||||
async fn resolve<R: Resolver>(&self, resolver: &R) -> Result<Cow<Self::Output>, R::Error>;
|
||||
|
||||
fn unresolve(&self) -> Option<&str>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! def_ld {
|
||||
($obj_type: expr, $x: ty) => {
|
||||
impl $crate::ObjectSingle for $x {
|
||||
fn get_type() -> &'static str {
|
||||
$obj_type
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ld_document {
|
||||
($y: ident, $x: ty) => {
|
||||
impl $y {
|
||||
pub async fn resolve<R: $crate::Resolver>(
|
||||
&self,
|
||||
resolver: &R,
|
||||
) -> Result<std::borrow::Cow<ObjectRaw<$x>>, R::Error> {
|
||||
match self {
|
||||
$y::Object(obj) => Ok(std::borrow::Cow::Borrowed(obj)),
|
||||
$y::Remote(Id(link)) => {
|
||||
Ok(std::borrow::Cow::Owned(resolver.resolve(link).await?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unresolve(&self) -> Option<&str> {
|
||||
match self {
|
||||
$y::Object(obj) => obj.as_data().as_id.as_deref(),
|
||||
$y::Remote(Id(id)) => Some(&id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum $y {
|
||||
Remote(Id),
|
||||
Object(Box<ObjectRaw<$x>>),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl $crate::Resolvable for $y {
|
||||
type Output = $crate::ObjectRaw<$x>;
|
||||
|
||||
async fn resolve<R: $crate::Resolver>(
|
||||
&self,
|
||||
resolver: &R,
|
||||
) -> Result<std::borrow::Cow<Self::Output>, R::Error> {
|
||||
self.resolve(resolver).await
|
||||
}
|
||||
|
||||
fn unresolve(&self) -> Option<&str> {
|
||||
self.unresolve()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl ObjectRaw<$x> {
|
||||
pub fn try_cast<T>() -> T {}
|
||||
}
|
||||
|
||||
*/
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ld_union {
|
||||
($y: ident, $z: ident, $($item_name: ident as $item_type: ty),+) => {
|
||||
impl $y {
|
||||
pub async fn resolve<R: $crate::Resolver>(
|
||||
&self,
|
||||
resolver: &R,
|
||||
) -> Result<std::borrow::Cow<ObjectRaw<$z>>, R::Error> {
|
||||
match self {
|
||||
$y::Object(obj) => Ok(std::borrow::Cow::Borrowed(obj)),
|
||||
$y::Remote(Id(link)) => {
|
||||
Ok(std::borrow::Cow::Owned(resolver.resolve(link).await?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unresolve(&self) -> Option<&str> {
|
||||
match self {
|
||||
$y::Object(obj) => obj.as_data().base.as_id.as_deref(),
|
||||
$y::Remote(Id(id)) => Some(&id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct $z {
|
||||
#[serde(flatten)]
|
||||
pub base: $crate::Base,
|
||||
$(
|
||||
#[serde(flatten)]
|
||||
$item_name: Box<$item_type>,
|
||||
)+
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum $y {
|
||||
Remote(Id),
|
||||
Object(ObjectRaw<$z>),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl $crate::Resolvable for $y {
|
||||
type Output = $crate::ObjectRaw<$z>;
|
||||
|
||||
async fn resolve<R: $crate::Resolver>(
|
||||
&self,
|
||||
resolver: &R,
|
||||
) -> Result<std::borrow::Cow<Self::Output>, R::Error> {
|
||||
self.resolve(resolver).await
|
||||
}
|
||||
|
||||
fn unresolve(&self) -> Option<&str> {
|
||||
self.unresolve()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OneOrMore<T> {
|
||||
One(T),
|
||||
Many(Vec<T>),
|
||||
}
|
||||
|
||||
impl<T> OneOrMore<T> {
|
||||
pub fn into_vec(self) -> Vec<T> {
|
||||
match self {
|
||||
OneOrMore::One(x) => vec![x],
|
||||
OneOrMore::Many(x) => x,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, T> {
|
||||
match self {
|
||||
OneOrMore::One(x) => std::slice::from_ref(x).iter(),
|
||||
OneOrMore::Many(x) => x.iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Base {
|
||||
#[serde(rename = "@type")]
|
||||
as_type: Option<OneOrMore<String>>,
|
||||
#[serde(rename = "@id")]
|
||||
as_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum LinkOrUrl {
|
||||
Url(Url),
|
||||
Link(Box<Link>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct Id(pub String);
|
|
@ -1,240 +0,0 @@
|
|||
use crate::object::{Object, RefObjectLinkUnion};
|
||||
use crate::{def_ld, ld_document, Id, ObjectRaw, ObjectSingle, OneOrMore};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct Activity {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#actor")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub actor: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub object: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#target")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub target: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#result")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub result: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#origin")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub origin: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#instrument")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub instrument: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
}
|
||||
|
||||
def_ld!("Activity", Activity, base as Object);
|
||||
ld_document!(RefActivity, Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct IntransitiveActivity;
|
||||
|
||||
def_ld!(
|
||||
"IntransitiveActivity",
|
||||
IntransitiveActivity,
|
||||
base as Activity
|
||||
);
|
||||
ld_document!(RefIntransitiveActivity, IntransitiveActivity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityAccept;
|
||||
|
||||
def_ld!("Accept", ActivityAccept, base as Activity);
|
||||
ld_document!(RefActivityAccept, ActivityAccept);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityTentativeAccept;
|
||||
|
||||
def_ld!(
|
||||
"TentativeAccept",
|
||||
ActivityTentativeAccept,
|
||||
base as ActivityAccept
|
||||
);
|
||||
ld_document!(RefActivityTentativeAccept, ActivityTentativeAccept);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityAdd;
|
||||
|
||||
def_ld!("Add", ActivityAdd, base as Activity);
|
||||
ld_document!(RefActivityAdd, ActivityAdd);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityArrive;
|
||||
|
||||
def_ld!("Arrive", ActivityArrive, base as Activity);
|
||||
ld_document!(RefActivityArrive, ActivityArrive);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityCreate;
|
||||
|
||||
def_ld!("Create", ActivityCreate, base as Activity);
|
||||
ld_document!(RefActivityCreate, ActivityCreate);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityDelete;
|
||||
|
||||
def_ld!("Delete", ActivityDelete, base as Activity);
|
||||
ld_document!(RefActivityDelete, ActivityDelete);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityFollow;
|
||||
|
||||
def_ld!("Follow", ActivityFollow, base as Activity);
|
||||
ld_document!(RefActivityFollow, ActivityFollow);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityIgnore;
|
||||
|
||||
def_ld!("Ignore", ActivityIgnore, base as Activity);
|
||||
ld_document!(RefActivityIgnore, ActivityIgnore);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityJoin;
|
||||
|
||||
def_ld!("Join", ActivityJoin, base as Activity);
|
||||
ld_document!(RefActivityJoin, ActivityJoin);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityLeave;
|
||||
|
||||
def_ld!("Leave", ActivityLeave, base as Activity);
|
||||
ld_document!(RefActivityLeave, ActivityLeave);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityLike;
|
||||
|
||||
def_ld!("Like", ActivityLike, base as Activity);
|
||||
ld_document!(RefActivityLike, ActivityLike);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityOffer;
|
||||
|
||||
def_ld!("Offer", ActivityOffer, base as Activity);
|
||||
ld_document!(RefActivityOffer, ActivityOffer);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityInvite;
|
||||
|
||||
def_ld!("Invite", ActivityInvite, base as ActivityOffer);
|
||||
ld_document!(RefActivityInvite, ActivityInvite);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityReject;
|
||||
|
||||
def_ld!("Reject", ActivityReject, base as Activity);
|
||||
ld_document!(RefActivityReject, ActivityReject);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityTentativeReject;
|
||||
|
||||
def_ld!(
|
||||
"TentativeReject",
|
||||
ActivityTentativeReject,
|
||||
base as ActivityReject
|
||||
);
|
||||
ld_document!(RefActivityTentativeReject, ActivityTentativeReject);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityRemove;
|
||||
|
||||
def_ld!("Remove", ActivityRemove, base as Activity);
|
||||
ld_document!(RefActivityRemove, ActivityRemove);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityUndo;
|
||||
|
||||
def_ld!("Undo", ActivityUndo, base as Activity);
|
||||
ld_document!(RefActivityUndo, ActivityUndo);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityUpdate;
|
||||
|
||||
def_ld!("Update", ActivityUpdate, base as Activity);
|
||||
ld_document!(RefActivityUpdate, ActivityUpdate);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityView;
|
||||
|
||||
def_ld!("View", ActivityView, base as Activity);
|
||||
ld_document!(RefActivityView, ActivityView);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityListen;
|
||||
|
||||
def_ld!("Listen", ActivityListen, base as Activity);
|
||||
ld_document!(RefActivityListen, ActivityListen);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityRead;
|
||||
|
||||
def_ld!("Read", ActivityRead, base as Activity);
|
||||
ld_document!(RefActivityRead, ActivityRead);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityMove;
|
||||
|
||||
def_ld!("Move", ActivityMove, base as Activity);
|
||||
ld_document!(RefActivityMove, ActivityMove);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityTravel;
|
||||
|
||||
def_ld!("Travel", ActivityTravel, base as IntransitiveActivity);
|
||||
ld_document!(RefActivityTravel, ActivityTravel);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityAnnounce;
|
||||
|
||||
def_ld!("Announce", ActivityAnnounce, base as Activity);
|
||||
ld_document!(RefActivityAnnounce, ActivityAnnounce);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityBlock;
|
||||
|
||||
def_ld!("Block", ActivityBlock, base as ActivityIgnore);
|
||||
ld_document!(RefActivityBlock, ActivityBlock);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityFlag;
|
||||
|
||||
def_ld!("Flag", ActivityFlag, base as Activity);
|
||||
ld_document!(RefActivityFlag, ActivityFlag);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityDislike;
|
||||
|
||||
def_ld!("Dislike", ActivityDislike, base as Activity);
|
||||
ld_document!(RefActivityDislike, ActivityDislike);
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ActivityClosedStatus {
|
||||
Date(DateTime<Utc>),
|
||||
Bool(bool),
|
||||
Other(RefObjectLinkUnion),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityQuestion {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#oneOf")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub one_of: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#anyOf")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub any_of: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#closed")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub closed: Option<OneOrMore<ActivityClosedStatus>>,
|
||||
}
|
||||
|
||||
def_ld!("Question", ActivityQuestion, base as IntransitiveActivity);
|
||||
ld_document!(RefActivityQuestion, ActivityQuestion);
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "magnetar_activity_pub"
|
||||
name = "magnetar_activity_streams"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -9,11 +9,14 @@ crate-type = ["rlib"]
|
|||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
either = { workspace = true }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio = { workspace = true, features = ["full"] }
|
|
@ -0,0 +1,88 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use crate::link::Link;
|
||||
|
||||
pub mod link;
|
||||
pub mod object;
|
||||
pub mod recipe;
|
||||
|
||||
pub trait ObjectTyped: Clone + Debug + Sized + 'static {
|
||||
fn get_type() -> &'static str;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! def_ld {
|
||||
($obj_type: expr, $x: ty) => {
|
||||
impl $crate::ObjectTyped for $x {
|
||||
fn get_type() -> &'static str {
|
||||
$obj_type
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ld_union {
|
||||
($z: ident, $($item_name: ident as $item_type: ty),+) => {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct $z {
|
||||
$(
|
||||
#[serde(flatten)]
|
||||
$item_name: Box<$item_type>,
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OneOrMore<T> {
|
||||
One(T),
|
||||
Many(Vec<T>),
|
||||
}
|
||||
|
||||
impl<T> OneOrMore<T> {
|
||||
pub fn into_vec(self) -> Vec<T> {
|
||||
match self {
|
||||
OneOrMore::One(x) => vec![x],
|
||||
OneOrMore::Many(x) => x,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, T> {
|
||||
match self {
|
||||
OneOrMore::One(x) => std::slice::from_ref(x).iter(),
|
||||
OneOrMore::Many(x) => x.iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Base {
|
||||
#[serde(rename = "@type")]
|
||||
as_type: Option<OneOrMore<String>>,
|
||||
#[serde(rename = "@id")]
|
||||
as_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum LinkOrUrl<'a> {
|
||||
Url(Url),
|
||||
Link(Box<Link<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct Id(pub String);
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ObjectSlot<'a, T: ObjectTyped> {
|
||||
Id(Id),
|
||||
Value(Cow<'a, T>),
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{def_ld, ld_document, Id, ObjectRaw};
|
||||
use crate::def_ld;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct LinkMention;
|
||||
pub struct LinkMention {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Mention", LinkMention);
|
||||
ld_document!(RefLinkMention, LinkMention);
|
|
@ -1,12 +1,13 @@
|
|||
use crate::object::RefObjectLinkUnion;
|
||||
use crate::OneOrMore;
|
||||
use crate::{def_ld, ld_document, Id, ObjectRaw};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{def_ld, ObjectSlot};
|
||||
use crate::object::ObjectLinkUnion;
|
||||
use crate::OneOrMore;
|
||||
|
||||
pub mod link_types;
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct Link {
|
||||
pub struct Link<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#href")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub href: Option<String>,
|
||||
|
@ -37,8 +38,7 @@ pub struct Link {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub preview: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub preview: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Link", Link);
|
||||
ld_document!(RefLink, Link);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Link", Link<'_>);
|
|
@ -0,0 +1,211 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{def_ld, ObjectSlot, ObjectTyped, OneOrMore};
|
||||
use crate::object::ObjectLinkUnion;
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct Activity<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#actor")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub actor: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub object: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#target")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub target: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#result")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub result: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#origin")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub origin: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#instrument")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub instrument: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Activity", Activity<'_>, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct IntransitiveActivity {}
|
||||
|
||||
def_ld!(
|
||||
"https://www.w3.org/ns/activitystreams#IntransitiveActivity",
|
||||
IntransitiveActivity,
|
||||
base as Activity
|
||||
);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityAccept {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Accept", ActivityAccept, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityTentativeAccept {}
|
||||
|
||||
def_ld!(
|
||||
"https://www.w3.org/ns/activitystreams#TentativeAccept",
|
||||
ActivityTentativeAccept,
|
||||
base as ActivityAccept
|
||||
);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityAdd;
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Add", ActivityAdd, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityArrive;
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Arrive", ActivityArrive, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityCreate;
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Create", ActivityCreate, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityDelete {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Delete", ActivityDelete, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityFollow {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Follow", ActivityFollow, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityIgnore {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Ignore", ActivityIgnore, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityJoin {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Join", ActivityJoin, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityLeave {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Leave", ActivityLeave, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityLike {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Like", ActivityLike, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityOffer {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Offer", ActivityOffer, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityInvite {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Invite", ActivityInvite, base as ActivityOffer);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityReject {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Reject", ActivityReject, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityTentativeReject {}
|
||||
|
||||
def_ld!(
|
||||
"https://www.w3.org/ns/activitystreams#TentativeReject",
|
||||
ActivityTentativeReject,
|
||||
base as ActivityReject
|
||||
);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityRemove {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Remove", ActivityRemove, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityUndo {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Undo", ActivityUndo, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityUpdate {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Update", ActivityUpdate, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityView {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#View", ActivityView, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityListen {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Listen", ActivityListen, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityRead {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Read", ActivityRead, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityMove {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Move", ActivityMove, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityTravel {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Travel", ActivityTravel, base as IntransitiveActivity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityAnnounce {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Announce", ActivityAnnounce, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityBlock {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Block", ActivityBlock, base as ActivityIgnore);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityFlag {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Flag", ActivityFlag, base as Activity);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityDislike {}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Dislike", ActivityDislike, base as Activity);
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ActivityClosedStatus<'a> {
|
||||
Date(DateTime<Utc>),
|
||||
Bool(bool),
|
||||
Other(ObjectSlot<'a, ObjectLinkUnion>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActivityQuestion<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#oneOf")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub one_of: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#anyOf")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub any_of: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#closed")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub closed: Option<OneOrMore<ActivityClosedStatus<'a>>>,
|
||||
}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Question", ActivityQuestion<'_>, base as IntransitiveActivity);
|
|
@ -1,32 +1,34 @@
|
|||
use crate::object::{RefCollection, RefOrderedCollection};
|
||||
use crate::{def_ld, ld_document, Id, ObjectRaw};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{def_ld, ObjectSlot};
|
||||
use crate::object::{Collection, ObjectLinkUnion, OrderedCollection};
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActorActivityPubProps {
|
||||
pub struct ActorActivityPubProps<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#inbox")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub inbox: Option<RefOrderedCollection>,
|
||||
pub inbox: Option<ObjectSlot<'a, OrderedCollection>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#outbox")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub outbox: Option<RefOrderedCollection>,
|
||||
pub outbox: Option<ObjectSlot<'a, OrderedCollection>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#following")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub following: Option<RefCollection>,
|
||||
pub following: Option<ObjectSlot<'a, Collection>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#followers")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub followers: Option<RefCollection>,
|
||||
pub followers: Option<ObjectSlot<'a, Collection>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#liked")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub liked: Option<RefCollection>,
|
||||
pub liked: Option<ObjectSlot<'a, Collection>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#shares")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub shares: Option<RefCollection>,
|
||||
pub shares: Option<ObjectSlot<'a, Collection>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#preferredUsername")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -34,35 +36,30 @@ pub struct ActorActivityPubProps {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#endpoints")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub endpoints: Option<String>,
|
||||
pub endpoints: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActorApplication;
|
||||
pub struct ActorApplication {}
|
||||
|
||||
def_ld!("Application", ActorApplication, base as Object);
|
||||
ld_document!(RefActorApplication, ActorApplication);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Application", ActorApplication, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActorGroup;
|
||||
pub struct ActorGroup {}
|
||||
|
||||
def_ld!("Group", ActorGroup, base as Object);
|
||||
ld_document!(RefActorGroup, ActorGroup);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Group", ActorGroup, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActorOrganization;
|
||||
pub struct ActorOrganization {}
|
||||
|
||||
def_ld!("Organization", ActorOrganization, base as Object);
|
||||
ld_document!(RefActorOrganization, ActorOrganization);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Organization", ActorOrganization, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActorPerson;
|
||||
pub struct ActorPerson {}
|
||||
|
||||
def_ld!("Person", ActorPerson, base as Object);
|
||||
ld_document!(RefActorPerson, ActorPerson);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Person", ActorPerson, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ActorService;
|
||||
pub struct ActorService {}
|
||||
|
||||
def_ld!("Service", ActorService, base as Object);
|
||||
ld_document!(RefActorService, ActorService);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Service", ActorService, base as Object);
|
|
@ -1,28 +1,30 @@
|
|||
use crate::link::Link;
|
||||
use crate::object::object_types::RefObjectImageLinkUnion;
|
||||
use crate::{def_ld, Id};
|
||||
use crate::{ld_document, ld_union, Base, LinkOrUrl, ObjectRaw, ObjectSingle, OneOrMore};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ld_union, LinkOrUrl, ObjectSlot, OneOrMore};
|
||||
use crate::def_ld;
|
||||
use crate::link::Link;
|
||||
use crate::object::object_types::ObjectImageLinkUnion;
|
||||
|
||||
pub mod activity;
|
||||
pub mod actor;
|
||||
pub mod object_types;
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct Object {
|
||||
pub struct Object<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#attachment")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub attachment: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub attachment: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#attributedTo")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub attributed_to: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub attributed_to: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#audience")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub audience: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub audience: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#content")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -34,7 +36,7 @@ pub struct Object {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#context")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub context: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub context: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#name")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -50,27 +52,27 @@ pub struct Object {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#generator")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub generator: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub generator: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#icon")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon: Option<OneOrMore<RefObjectImageLinkUnion>>,
|
||||
pub icon: Option<OneOrMore<ObjectSlot<'a, ObjectImageLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#image")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image: Option<OneOrMore<RefObjectImageLinkUnion>>,
|
||||
pub image: Option<OneOrMore<ObjectSlot<'a, ObjectImageLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#inReplyTo")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub in_reply_to: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub in_reply_to: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#location")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub location: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub preview: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub preview: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#published")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -78,7 +80,7 @@ pub struct Object {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#replies")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub replies: Option<RefCollectionLinkUnion>,
|
||||
pub replies: Option<ObjectSlot<'a, CollectionLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#startTime")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -94,7 +96,7 @@ pub struct Object {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#tag")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tag: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub tag: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#updated")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -102,23 +104,23 @@ pub struct Object {
|
|||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#url")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub url: Option<OneOrMore<LinkOrUrl>>,
|
||||
pub url: Option<OneOrMore<LinkOrUrl<'a>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#to")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub to: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub to: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#bto")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bto: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub bto: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#cc")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cc: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub cc: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#bcc")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bcc: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub bcc: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#mediaType")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -131,13 +133,11 @@ pub struct Object {
|
|||
// ActivityPub
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#source")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub source: Option<RefObject>,
|
||||
pub source: Option<ObjectSlot<'a, Object<'a>>>,
|
||||
}
|
||||
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Object", Object);
|
||||
ld_document!(RefObject, Object);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Object", Object<'_>);
|
||||
ld_union!(
|
||||
RefObjectLinkUnion,
|
||||
ObjectLinkUnion,
|
||||
object_props as Object,
|
||||
link_props as Link
|
||||
|
@ -170,9 +170,7 @@ def_ld!(
|
|||
"https://www.w3.org/ns/activitystreams#Collection",
|
||||
Collection
|
||||
);
|
||||
ld_document!(RefCollection, Collection);
|
||||
ld_union!(
|
||||
RefCollectionLinkUnion,
|
||||
CollectionLinkUnion,
|
||||
collection_props as Collection,
|
||||
link_props as Link
|
||||
|
@ -189,7 +187,6 @@ def_ld!(
|
|||
"https://www.w3.org/ns/activitystreams#OrderedCollection",
|
||||
OrderedCollection
|
||||
);
|
||||
ld_document!(RefOrderedCollection, OrderedCollection);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct CollectionPage {
|
||||
|
@ -210,10 +207,7 @@ def_ld!(
|
|||
"https://www.w3.org/ns/activitystreams#CollectionPage",
|
||||
CollectionPage
|
||||
);
|
||||
ld_document!(RefCollectionPage, CollectionPage);
|
||||
|
||||
ld_union!(
|
||||
RefCollectionPageLinkUnion,
|
||||
CollectionPageLinkUnion,
|
||||
page_props as CollectionPage,
|
||||
link_props as Link
|
||||
|
@ -230,4 +224,3 @@ def_ld!(
|
|||
"https://www.w3.org/ns/activitystreams#OrderedCollectionPage",
|
||||
OrderedCollectionPage
|
||||
);
|
||||
ld_document!(RefOrderedCollectionPage, OrderedCollectionPage);
|
|
@ -1,80 +1,71 @@
|
|||
use crate::link::Link;
|
||||
use crate::object::{Object, RefObject, RefObjectLinkUnion};
|
||||
use crate::{def_ld, ld_document, ld_union, Id, ObjectRaw, ObjectSingle, OneOrMore};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{def_ld, ld_union, ObjectSlot, ObjectTyped, OneOrMore};
|
||||
use crate::link::Link;
|
||||
use crate::object::{Object, ObjectLinkUnion};
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectRelationship {
|
||||
pub struct ObjectRelationship<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub object: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||
pub object: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#subject")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub subject: Option<RefObjectLinkUnion>,
|
||||
pub subject: Option<ObjectSlot<'a, ObjectLinkUnion>>,
|
||||
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#relationship")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub relationship: Option<OneOrMore<RefObject>>,
|
||||
pub relationship: Option<OneOrMore<ObjectSlot<'a, Object<'a>>>>,
|
||||
}
|
||||
|
||||
def_ld!("Relationship", ObjectRelationship, base as Object);
|
||||
ld_document!(RefObjectRelationship, ObjectRelationship);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Relationship", ObjectRelationship, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectArticle;
|
||||
pub struct ObjectArticle {}
|
||||
|
||||
def_ld!("Article", ObjectArticle, base as Object);
|
||||
ld_document!(RefObjectArticle, ObjectArticle);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Article", ObjectArticle, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectDocument;
|
||||
pub struct ObjectDocument {}
|
||||
|
||||
def_ld!("Document", ObjectDocument, base as Object);
|
||||
ld_document!(RefObjectDocument, ObjectDocument);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Document", ObjectDocument, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectAudio;
|
||||
pub struct ObjectAudio {}
|
||||
|
||||
def_ld!("Audio", ObjectAudio, base as ObjectDocument);
|
||||
ld_document!(RefObjectAudio, ObjectAudio);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Audio", ObjectAudio, base as ObjectDocument);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectImage;
|
||||
pub struct ObjectImage {}
|
||||
|
||||
def_ld!("Image", ObjectImage, base as ObjectDocument);
|
||||
ld_document!(RefObjectImage, ObjectImage);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Image", ObjectImage, base as ObjectDocument);
|
||||
ld_union!(
|
||||
RefObjectImageLinkUnion,
|
||||
ObjectImageLinkUnion,
|
||||
image_props as ObjectImage,
|
||||
link_props as Link
|
||||
);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectVideo;
|
||||
pub struct ObjectVideo {}
|
||||
|
||||
def_ld!("Video", ObjectVideo, base as ObjectDocument);
|
||||
ld_document!(RefObjectVideo, ObjectVideo);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Video", ObjectVideo, base as ObjectDocument);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectNote;
|
||||
pub struct ObjectNote {}
|
||||
|
||||
def_ld!("Note", ObjectNote, base as Object);
|
||||
ld_document!(RefObjectNote, ObjectNote);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Note", ObjectNote, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectPage;
|
||||
pub struct ObjectPage {}
|
||||
|
||||
def_ld!("Page", ObjectPage, base as ObjectDocument);
|
||||
ld_document!(RefObjectPage, ObjectPage);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Page", ObjectPage, base as ObjectDocument);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectEvent;
|
||||
pub struct ObjectEvent {}
|
||||
|
||||
def_ld!("Event", ObjectEvent, base as Object);
|
||||
ld_document!(RefObjectEvent, ObjectEvent);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Event", ObjectEvent, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectPlace {
|
||||
|
@ -103,18 +94,16 @@ pub struct ObjectPlace {
|
|||
pub units: Option<String>,
|
||||
}
|
||||
|
||||
def_ld!("Place", ObjectPlace, base as Object);
|
||||
ld_document!(RefObjectPlace, ObjectPlace);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Place", ObjectPlace, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectProfile {
|
||||
pub struct ObjectProfile<'a> {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#describes")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub describes: Option<RefObject>,
|
||||
pub describes: Option<ObjectSlot<'a, ObjectProfile<'a>>>,
|
||||
}
|
||||
|
||||
def_ld!("Profile", ObjectProfile, base as Object);
|
||||
ld_document!(RefObjectProfile, ObjectProfile);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Profile", ObjectProfile, base as Object);
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ObjectTombstone {
|
||||
|
@ -127,5 +116,4 @@ pub struct ObjectTombstone {
|
|||
pub deleted: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
def_ld!("Tombstone", ObjectTombstone, base as Object);
|
||||
ld_document!(RefObjectTombstone, ObjectTombstone);
|
||||
def_ld!("https://www.w3.org/ns/activitystreams#Tombstone", ObjectTombstone, base as Object);
|
|
@ -1,16 +1,9 @@
|
|||
use crate::{OneOrMore, Resolvable};
|
||||
use thiserror::Error;
|
||||
|
||||
#[macro_use]
|
||||
pub mod account_move;
|
||||
use crate::OneOrMore;
|
||||
|
||||
#[macro_use]
|
||||
pub mod block;
|
||||
#[macro_use]
|
||||
pub mod flag;
|
||||
#[macro_use]
|
||||
pub mod poll;
|
||||
#[macro_use]
|
||||
pub mod tag;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RecipeError {
|
|
@ -1,51 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "gallery_like")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "postId")]
|
||||
pub post_id: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::gallery_post::Entity",
|
||||
from = "Column::PostId",
|
||||
to = "super::gallery_post::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
GalleryPost,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::gallery_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::GalleryPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,54 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "gallery_post")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "fileIds")]
|
||||
pub file_ids: Vec<String>,
|
||||
#[sea_orm(column_name = "isSensitive")]
|
||||
pub is_sensitive: bool,
|
||||
#[sea_orm(column_name = "likedCount")]
|
||||
pub liked_count: i32,
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::gallery_like::Entity")]
|
||||
GalleryLike,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::gallery_like::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::GalleryLike.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,90 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use super::sea_orm_active_enums::PageVisibilityEnum;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "page")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
pub title: String,
|
||||
pub name: String,
|
||||
pub summary: Option<String>,
|
||||
#[sea_orm(column_name = "alignCenter")]
|
||||
pub align_center: bool,
|
||||
pub font: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "eyeCatchingImageId")]
|
||||
pub eye_catching_image_id: Option<String>,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub content: Json,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub variables: Json,
|
||||
pub visibility: PageVisibilityEnum,
|
||||
#[sea_orm(column_name = "visibleUserIds")]
|
||||
pub visible_user_ids: Vec<String>,
|
||||
#[sea_orm(column_name = "likedCount")]
|
||||
pub liked_count: i32,
|
||||
#[sea_orm(column_name = "hideTitleWhenPinned")]
|
||||
pub hide_title_when_pinned: bool,
|
||||
pub script: String,
|
||||
#[sea_orm(column_name = "isPublic")]
|
||||
pub is_public: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::drive_file::Entity",
|
||||
from = "Column::EyeCatchingImageId",
|
||||
to = "super::drive_file::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
DriveFile,
|
||||
#[sea_orm(has_many = "super::page_like::Entity")]
|
||||
PageLike,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(has_one = "super::user_profile::Entity")]
|
||||
UserProfile,
|
||||
}
|
||||
|
||||
impl Related<super::drive_file::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::DriveFile.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::page_like::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::PageLike.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_profile::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserProfile.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,51 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "page_like")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "pageId")]
|
||||
pub page_id: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::page::Entity",
|
||||
from = "Column::PageId",
|
||||
to = "super::page::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Page,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::page::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Page.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,62 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "user_group")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub name: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "isPrivate")]
|
||||
pub is_private: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(has_many = "super::user_group_invitation::Entity")]
|
||||
UserGroupInvitation,
|
||||
#[sea_orm(has_many = "super::user_group_invite::Entity")]
|
||||
UserGroupInvite,
|
||||
#[sea_orm(has_many = "super::user_group_joining::Entity")]
|
||||
UserGroupJoining,
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_invitation::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupInvitation.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_invite::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupInvite.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_joining::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupJoining.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,59 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "user_group_invitation")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userGroupId")]
|
||||
pub user_group_id: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::notification::Entity")]
|
||||
Notification,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user_group::Entity",
|
||||
from = "Column::UserGroupId",
|
||||
to = "super::user_group::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
UserGroup,
|
||||
}
|
||||
|
||||
impl Related<super::notification::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Notification.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroup.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,51 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "user_group_invite")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userGroupId")]
|
||||
pub user_group_id: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user_group::Entity",
|
||||
from = "Column::UserGroupId",
|
||||
to = "super::user_group::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
UserGroup,
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroup.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,59 +0,0 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "user_group_joining")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userGroupId")]
|
||||
pub user_group_id: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::antenna::Entity")]
|
||||
Antenna,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user_group::Entity",
|
||||
from = "Column::UserGroupId",
|
||||
to = "super::user_group::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
UserGroup,
|
||||
}
|
||||
|
||||
impl Related<super::antenna::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Antenna.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroup.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,20 +0,0 @@
|
|||
pub use sea_orm_migration::prelude::*;
|
||||
|
||||
mod m20220101_000001_bootstrap;
|
||||
mod m20230729_201733_drop_messaging_integrations;
|
||||
mod m20230729_212237_user_unique_idx;
|
||||
mod m20230806_142918_drop_featured_note_option;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigratorTrait for Migrator {
|
||||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||
vec![
|
||||
Box::new(m20220101_000001_bootstrap::Migration),
|
||||
Box::new(m20230729_201733_drop_messaging_integrations::Migration),
|
||||
Box::new(m20230729_212237_user_unique_idx::Migration),
|
||||
Box::new(m20230806_142918_drop_featured_note_option::Migration),
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
[package]
|
||||
name = "magnetar_federation"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
magnetar_core = { path = "../core" }
|
||||
magnetar_common = { path = "../magnetar_common" }
|
||||
magnetar_host_meta = { path = "../ext_host_meta" }
|
||||
magnetar_webfinger = { path = "../ext_webfinger" }
|
||||
|
||||
async-trait = { workspace = true }
|
||||
async-stream = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
futures-core = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
|
||||
quick-xml = { workspace = true, features = ["serialize"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
base64 = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
httpdate = { workspace = true }
|
||||
|
||||
indexmap = { workspace = true, features = ["serde"] }
|
||||
|
||||
thiserror = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
|
||||
http = { workspace = true }
|
||||
headers = { workspace = true }
|
||||
hyper = { workspace = true, features = ["full"] }
|
||||
percent-encoding = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["stream", "hickory-dns"] }
|
||||
|
||||
ed25519-dalek = { workspace = true, features = [
|
||||
"pem",
|
||||
"pkcs8",
|
||||
"signature",
|
||||
"digest",
|
||||
] }
|
||||
rsa = { workspace = true, features = ["sha2"] }
|
||||
sha2 = { workspace = true }
|
||||
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tracing-subscriber = { workspace = true }
|
||||
miette = { workspace = true, features = ["fancy"] }
|
|
@ -0,0 +1,482 @@
|
|||
use chrono::Utc;
|
||||
use futures::TryFutureExt;
|
||||
use http::{HeaderMap, HeaderName, HeaderValue, Method};
|
||||
use indexmap::IndexSet;
|
||||
use serde_json::Value;
|
||||
use sha2::Digest;
|
||||
use std::{fmt::Display, string::FromUtf8Error, sync::Arc};
|
||||
use thiserror::Error;
|
||||
use tokio::task;
|
||||
use tokio::task::JoinError;
|
||||
use url::Url;
|
||||
|
||||
use magnetar_core::web_model::content_type::ContentActivityStreams;
|
||||
|
||||
use crate::{
|
||||
client::federation_client::{FederationClient, FederationClientError},
|
||||
crypto::{ApSigningError, ApSigningKey, SigningAlgorithm},
|
||||
ApClientService, ApSignature, ApSigningField, ApSigningHeaders, SigningInput, SigningParts,
|
||||
};
|
||||
|
||||
pub struct ApClientServiceDefaultProvider {
|
||||
client: Arc<dyn AsRef<FederationClient> + Send + Sync>,
|
||||
}
|
||||
|
||||
impl ApClientServiceDefaultProvider {
|
||||
pub fn new(client: impl AsRef<FederationClient> + Send + Sync + 'static) -> Self {
|
||||
Self {
|
||||
client: Arc::new(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ApSignature {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "keyId=\"{}\"", self.key_id)?;
|
||||
if let Some(ref algorithm) = self.algorithm {
|
||||
write!(f, ",algorithm=\"{}\"", algorithm)?;
|
||||
}
|
||||
if let Some(ref created) = self.created {
|
||||
write!(f, ",created={}", created.timestamp())?;
|
||||
}
|
||||
if let Some(ref expires) = self.expires {
|
||||
write!(f, ",expires={}", expires.timestamp())?;
|
||||
}
|
||||
if let Some(ref headers) = self.headers {
|
||||
write!(f, ",headers=\"{}\"", headers)?;
|
||||
}
|
||||
write!(f, ",signature=\"{}\"", self.signature)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ApSigningHeaders {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.0
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ApClientError {
|
||||
#[error("Federation client error: {0}")]
|
||||
FederationClientError(#[from] FederationClientError),
|
||||
#[error("Signing error: {0}")]
|
||||
SigningError(#[from] ApSigningError),
|
||||
#[error("URL parse error: {0}")]
|
||||
UrlParseError(#[from] url::ParseError),
|
||||
#[error("Failed to serialize JSON: {0}")]
|
||||
SerializerError(#[from] serde_json::Error),
|
||||
#[error("Invalid header value: {0}")]
|
||||
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
|
||||
#[error("UTF-8 parse error: {0}")]
|
||||
Utf8ParseError(#[from] FromUtf8Error),
|
||||
#[error("Task join error: {0}")]
|
||||
JoinError(#[from] JoinError),
|
||||
}
|
||||
|
||||
trait CreateField {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RequestTarget<'a> {
|
||||
url: &'a Url,
|
||||
method: Method,
|
||||
}
|
||||
|
||||
impl CreateField for RequestTarget<'_> {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
Some((
|
||||
ApSigningField::PseudoRequestTarget,
|
||||
format!(
|
||||
"{} {}",
|
||||
self.method.as_str().to_lowercase(),
|
||||
self.url.path()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HostPseudoHeader<'a>(&'a Url);
|
||||
|
||||
impl CreateField for HostPseudoHeader<'_> {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
Some((ApSigningField::Host, self.0.host()?.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DateHeader(chrono::DateTime<Utc>);
|
||||
|
||||
impl CreateField for DateHeader {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
Some((ApSigningField::Date, httpdate::fmt_http_date(self.0.into())))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CreatedPseudoHeader(chrono::DateTime<Utc>);
|
||||
|
||||
impl CreateField for CreatedPseudoHeader {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
Some((
|
||||
ApSigningField::PseudoCreated,
|
||||
self.0.timestamp().to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExpiresPseudoHeader(chrono::DateTime<Utc>);
|
||||
|
||||
impl CreateField for ExpiresPseudoHeader {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
Some((
|
||||
ApSigningField::PseudoExpires,
|
||||
self.0.timestamp().to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DigestHeader<'a>(&'a str);
|
||||
|
||||
impl CreateField for DigestHeader<'_> {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
Some((ApSigningField::Digest, self.0.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CreateField> CreateField for Option<T> {
|
||||
fn create_field(&self) -> Option<(ApSigningField, String)> {
|
||||
self.as_ref().and_then(T::create_field)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! signing_input {
|
||||
($name:ident, $($field_name:ident => $field_type:path),+ ) => {
|
||||
#[derive(Debug)]
|
||||
struct $name<'a> {
|
||||
$($field_name: $field_type),+
|
||||
}
|
||||
|
||||
impl SigningInput for $name<'_> {
|
||||
fn create_signing_input(&self) -> Vec<(ApSigningField, String)> {
|
||||
[$(self.$field_name.create_field()),+].into_iter().flatten().collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing_input!(SigningInputGetRsaSha256,
|
||||
request_target => RequestTarget<'a>,
|
||||
host => HostPseudoHeader<'a>,
|
||||
date => DateHeader,
|
||||
expires => Option<ExpiresPseudoHeader>);
|
||||
|
||||
impl SigningParts for SigningInputGetRsaSha256<'_> {
|
||||
fn get_created(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_expires(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
self.expires.as_ref().map(|ExpiresPseudoHeader(v)| v)
|
||||
}
|
||||
}
|
||||
|
||||
signing_input!(SigningInputGetHs2019,
|
||||
request_target => RequestTarget<'a>,
|
||||
host => HostPseudoHeader<'a>,
|
||||
created => CreatedPseudoHeader,
|
||||
expires => Option<ExpiresPseudoHeader>);
|
||||
|
||||
impl SigningParts for SigningInputGetHs2019<'_> {
|
||||
fn get_created(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
Some(&self.created.0)
|
||||
}
|
||||
|
||||
fn get_expires(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
self.expires.as_ref().map(|ExpiresPseudoHeader(v)| v)
|
||||
}
|
||||
}
|
||||
|
||||
signing_input!(SigningInputPostRsaSha256,
|
||||
request_target => RequestTarget<'a>,
|
||||
host => HostPseudoHeader<'a>,
|
||||
date => DateHeader,
|
||||
digest => DigestHeader<'a>,
|
||||
expires => Option<ExpiresPseudoHeader>);
|
||||
|
||||
impl SigningParts for SigningInputPostRsaSha256<'_> {
|
||||
fn get_created(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_expires(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
self.expires.as_ref().map(|ExpiresPseudoHeader(v)| v)
|
||||
}
|
||||
}
|
||||
|
||||
signing_input!(SigningInputPostHs2019,
|
||||
request_target => RequestTarget<'a>,
|
||||
host => HostPseudoHeader<'a>,
|
||||
created => CreatedPseudoHeader,
|
||||
digest => DigestHeader<'a>,
|
||||
expires => Option<ExpiresPseudoHeader>);
|
||||
|
||||
impl SigningParts for SigningInputPostHs2019<'_> {
|
||||
fn get_created(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
Some(&self.created.0)
|
||||
}
|
||||
|
||||
fn get_expires(&self) -> Option<&chrono::prelude::DateTime<Utc>> {
|
||||
self.expires.as_ref().map(|ExpiresPseudoHeader(v)| v)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ApClientService for ApClientServiceDefaultProvider {
|
||||
type Error = ApClientError;
|
||||
|
||||
async fn sign_request(
|
||||
&self,
|
||||
signing_key: ApSigningKey<'_>,
|
||||
signing_algorithm: SigningAlgorithm,
|
||||
request: &dyn SigningInput,
|
||||
) -> Result<ApSignature, Self::Error> {
|
||||
let components = request.create_signing_input();
|
||||
|
||||
let message = components
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{}: {}", k.as_ref(), v))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let key_id = signing_key.key_id.clone().into_owned();
|
||||
let key = signing_key.into_owned();
|
||||
let signature = task::spawn_blocking(move || {
|
||||
key
|
||||
.key
|
||||
.sign_base64(signing_algorithm, &message.into_bytes())
|
||||
}).await??;
|
||||
|
||||
Ok(ApSignature {
|
||||
key_id,
|
||||
algorithm: Some(signing_algorithm),
|
||||
created: request.get_created().cloned(),
|
||||
expires: request.get_expires().cloned(),
|
||||
headers: Some(ApSigningHeaders(IndexSet::from_iter(
|
||||
components.iter().map(|(k, _)| k).copied(),
|
||||
))),
|
||||
signature,
|
||||
})
|
||||
}
|
||||
|
||||
async fn signed_get(
|
||||
&self,
|
||||
signing_key: ApSigningKey<'_>,
|
||||
signing_algorithm: SigningAlgorithm,
|
||||
expires: Option<chrono::DateTime<Utc>>,
|
||||
url: &str,
|
||||
) -> Result<Value, Self::Error> {
|
||||
let url = url.parse()?;
|
||||
|
||||
let time_created = Utc::now();
|
||||
let signed = match signing_algorithm {
|
||||
SigningAlgorithm::RsaSha256 => self.sign_request(
|
||||
signing_key,
|
||||
signing_algorithm, &SigningInputGetRsaSha256 {
|
||||
request_target: RequestTarget {
|
||||
url: &url,
|
||||
method: Method::GET,
|
||||
},
|
||||
host: HostPseudoHeader(&url),
|
||||
date: DateHeader(time_created),
|
||||
expires: expires.map(ExpiresPseudoHeader),
|
||||
},
|
||||
).await?,
|
||||
SigningAlgorithm::Hs2019 => self.sign_request(
|
||||
signing_key,
|
||||
signing_algorithm,
|
||||
&SigningInputGetHs2019 {
|
||||
request_target: RequestTarget {
|
||||
url: &url,
|
||||
method: Method::GET,
|
||||
},
|
||||
host: HostPseudoHeader(&url),
|
||||
created: CreatedPseudoHeader(time_created),
|
||||
expires: expires.map(ExpiresPseudoHeader),
|
||||
},
|
||||
).await?,
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::Hs2019) {
|
||||
headers.insert(
|
||||
http::header::DATE,
|
||||
HeaderValue::try_from(httpdate::fmt_http_date(time_created.into()))
|
||||
.expect("date should always be a valid header value"),
|
||||
);
|
||||
}
|
||||
|
||||
headers.insert(
|
||||
HeaderName::from_lowercase(b"signature").unwrap(),
|
||||
HeaderValue::try_from(signed.to_string())?,
|
||||
);
|
||||
|
||||
Ok(self
|
||||
.client
|
||||
.as_ref()
|
||||
.as_ref()
|
||||
.get(url)
|
||||
.accept(ContentActivityStreams)
|
||||
.headers(headers)
|
||||
.json()
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn signed_post(
|
||||
&self,
|
||||
signing_key: ApSigningKey<'_>,
|
||||
signing_algorithm: SigningAlgorithm,
|
||||
expires: Option<chrono::DateTime<Utc>>,
|
||||
url: &str,
|
||||
body: &Value,
|
||||
) -> Result<String, Self::Error> {
|
||||
let url = url.parse()?;
|
||||
let body_bytes = serde_json::to_vec(body)?;
|
||||
// Move in, move out :3
|
||||
let (digest_raw, body_bytes) = task::spawn_blocking(move || {
|
||||
let mut sha = sha2::Sha256::new();
|
||||
sha.update(&body_bytes);
|
||||
(sha.finalize(), body_bytes)
|
||||
}).await?;
|
||||
|
||||
use base64::prelude::*;
|
||||
let digest_base64 = format!("sha-256={}", BASE64_STANDARD.encode(digest_raw));
|
||||
let time_created = Utc::now();
|
||||
let signed = match signing_algorithm {
|
||||
SigningAlgorithm::RsaSha256 => self.sign_request(
|
||||
signing_key,
|
||||
signing_algorithm,
|
||||
&SigningInputPostRsaSha256 {
|
||||
request_target: RequestTarget {
|
||||
url: &url,
|
||||
method: Method::POST,
|
||||
},
|
||||
host: HostPseudoHeader(&url),
|
||||
date: DateHeader(time_created),
|
||||
digest: DigestHeader(&digest_base64),
|
||||
expires: expires.map(ExpiresPseudoHeader),
|
||||
},
|
||||
).await?,
|
||||
SigningAlgorithm::Hs2019 => self.sign_request(
|
||||
signing_key,
|
||||
signing_algorithm,
|
||||
&SigningInputPostHs2019 {
|
||||
request_target: RequestTarget {
|
||||
url: &url,
|
||||
method: Method::POST,
|
||||
},
|
||||
host: HostPseudoHeader(&url),
|
||||
created: CreatedPseudoHeader(time_created),
|
||||
digest: DigestHeader(&digest_base64),
|
||||
expires: expires.map(ExpiresPseudoHeader),
|
||||
},
|
||||
).await?,
|
||||
};
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
if !matches!(signing_algorithm, SigningAlgorithm::Hs2019) {
|
||||
headers.insert(
|
||||
http::header::DATE,
|
||||
HeaderValue::try_from(httpdate::fmt_http_date(time_created.into()))
|
||||
.expect("date should always be a valid header value"),
|
||||
);
|
||||
}
|
||||
|
||||
headers.insert(
|
||||
HeaderName::from_lowercase(b"digest").unwrap(),
|
||||
HeaderValue::try_from(digest_base64)?,
|
||||
);
|
||||
|
||||
headers.insert(
|
||||
HeaderName::from_lowercase(b"signature").unwrap(),
|
||||
HeaderValue::try_from(signed.to_string())?,
|
||||
);
|
||||
|
||||
Ok(self
|
||||
.client
|
||||
.as_ref()
|
||||
.as_ref()
|
||||
.builder(Method::POST, url)
|
||||
.content_type(ContentActivityStreams)
|
||||
.headers(headers)
|
||||
.body(body_bytes)
|
||||
.send()
|
||||
.map_ok(String::from_utf8)
|
||||
.await??
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use headers::UserAgent;
|
||||
use miette::IntoDiagnostic;
|
||||
use rsa::pkcs8::DecodePrivateKey;
|
||||
|
||||
use crate::{
|
||||
ap_client::ApClientServiceDefaultProvider,
|
||||
client::federation_client::FederationClient,
|
||||
crypto::{ApHttpPrivateKey, SigningAlgorithm},
|
||||
ApClientService,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn should_request() -> miette::Result<()> {
|
||||
let key_id = std::env::var("MAG_TEST_KEY_ID").into_diagnostic()?;
|
||||
let key = std::env::var("MAG_TEST_PRIVATE_KEY").into_diagnostic()?;
|
||||
let url = std::env::var("MAG_TEST_FETCH_URL").into_diagnostic()?;
|
||||
|
||||
let rsa_key = rsa::RsaPrivateKey::from_pkcs8_pem(key.trim()).into_diagnostic()?;
|
||||
let ap_client = ApClientServiceDefaultProvider {
|
||||
client: Arc::new(Box::new(
|
||||
FederationClient::new(
|
||||
true,
|
||||
128_000,
|
||||
25,
|
||||
UserAgent::from_static("magnetar/0.42 (https://astolfo.social)"),
|
||||
)
|
||||
.into_diagnostic()?,
|
||||
)),
|
||||
};
|
||||
|
||||
let val = ap_client
|
||||
.signed_get(
|
||||
ApHttpPrivateKey::Rsa(Cow::Owned(Box::new(rsa_key)))
|
||||
.create_signing_key(&key_id, SigningAlgorithm::RsaSha256)
|
||||
.into_diagnostic()?,
|
||||
SigningAlgorithm::RsaSha256,
|
||||
None,
|
||||
&url,
|
||||
)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
println!("{:#?}", val);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
use async_stream::stream;
|
||||
use futures_util::{select, stream::StreamExt, FutureExt, Stream, TryStreamExt};
|
||||
use headers::UserAgent;
|
||||
use hyper::body::Bytes;
|
||||
use reqwest::{redirect::Policy, Client, RequestBuilder};
|
||||
use serde_json::Value;
|
||||
use thiserror::Error;
|
||||
use tokio::pin;
|
||||
use url::Url;
|
||||
|
||||
use magnetar_core::web_model::ContentType;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FederationClient {
|
||||
pub client: Client,
|
||||
pub body_limit: usize,
|
||||
pub timeout_seconds: u64,
|
||||
user_agent: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FederationClientBuilderError {
|
||||
#[error("Reqwest error: {0}")]
|
||||
ReqwestError(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FederationClientError {
|
||||
#[error("Fetch timed out")]
|
||||
TimeoutError,
|
||||
#[error("Reqwest error: {0}")]
|
||||
ReqwestError(#[from] reqwest::Error),
|
||||
#[error("JSON error: {0}")]
|
||||
JsonError(#[from] serde_json::Error),
|
||||
#[error("XML error: {0}")]
|
||||
XmlError(#[from] quick_xml::de::DeError),
|
||||
#[error("Body limit exceeded error")]
|
||||
BodyLimitExceededError,
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidUrl(#[from] url::ParseError),
|
||||
#[error("Client error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FederationRequestBuilder<'a> {
|
||||
client: &'a FederationClient,
|
||||
builder: RequestBuilder,
|
||||
}
|
||||
|
||||
impl FederationClient {
|
||||
pub fn new(
|
||||
force_https: bool,
|
||||
body_limit: usize,
|
||||
timeout_seconds: u64,
|
||||
user_agent: UserAgent,
|
||||
) -> Result<FederationClient, FederationClientBuilderError> {
|
||||
let client = Client::builder()
|
||||
.https_only(force_https)
|
||||
.redirect(Policy::limited(5))
|
||||
.build()?;
|
||||
|
||||
Ok(FederationClient {
|
||||
client,
|
||||
body_limit,
|
||||
timeout_seconds,
|
||||
user_agent: user_agent.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn builder(&self, method: reqwest::Method, url: Url) -> FederationRequestBuilder<'_> {
|
||||
FederationRequestBuilder {
|
||||
client: self,
|
||||
builder: self
|
||||
.client
|
||||
.request(method, url)
|
||||
.header(http::header::USER_AGENT, self.user_agent.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, url: Url) -> FederationRequestBuilder<'_> {
|
||||
self.builder(reqwest::Method::GET, url)
|
||||
}
|
||||
}
|
||||
|
||||
impl FederationRequestBuilder<'_> {
|
||||
pub fn content_type(self, content_type: impl ContentType) -> Self {
|
||||
Self {
|
||||
client: self.client,
|
||||
builder: self.builder.header(
|
||||
http::header::CONTENT_TYPE,
|
||||
content_type.mime_type().to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn accept(self, content_type: impl ContentType) -> Self {
|
||||
Self {
|
||||
client: self.client,
|
||||
builder: self
|
||||
.builder
|
||||
.header(http::header::ACCEPT, content_type.mime_type().to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn headers(self, headers: reqwest::header::HeaderMap) -> Self {
|
||||
Self {
|
||||
client: self.client,
|
||||
builder: self.builder.headers(headers),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body(self, body: Vec<u8>) -> Self {
|
||||
Self {
|
||||
client: self.client,
|
||||
builder: self.builder.body(body),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_stream(
|
||||
self,
|
||||
) -> Result<impl Stream<Item = Result<Bytes, FederationClientError>>, FederationClientError>
|
||||
{
|
||||
let mut body = self
|
||||
.builder
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.bytes_stream()
|
||||
.map(|b| b.map_err(FederationClientError::ReqwestError));
|
||||
|
||||
let body_limit = self.client.body_limit;
|
||||
let mut partial_length: usize = 0;
|
||||
Ok(stream! {
|
||||
while let Some(chunk) = body.next().await.transpose()? {
|
||||
if partial_length + chunk.len() > body_limit {
|
||||
yield Err(FederationClientError::BodyLimitExceededError);
|
||||
}
|
||||
|
||||
partial_length += chunk.len();
|
||||
yield Ok(chunk);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send(self) -> Result<Vec<u8>, FederationClientError> {
|
||||
let sleep = tokio::time::sleep(tokio::time::Duration::from_secs(
|
||||
self.client.timeout_seconds,
|
||||
))
|
||||
.fuse();
|
||||
tokio::pin!(sleep);
|
||||
|
||||
let body = async move {
|
||||
self.send_stream()
|
||||
.await?
|
||||
.try_fold(Vec::new(), |mut acc, b| async move {
|
||||
acc.extend_from_slice(&b);
|
||||
Ok(acc)
|
||||
})
|
||||
.await
|
||||
}
|
||||
.fuse();
|
||||
|
||||
pin!(body);
|
||||
|
||||
select! {
|
||||
b = body => b,
|
||||
_ = sleep => Err(FederationClientError::TimeoutError)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn json(self) -> Result<Value, FederationClientError> {
|
||||
let data = self.send().await?;
|
||||
let json = serde_json::from_slice::<Value>(&data)?;
|
||||
|
||||
Ok(json)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod federation_client;
|
|
@ -0,0 +1,323 @@
|
|||
use rsa::pkcs1::DecodeRsaPrivateKey;
|
||||
use rsa::pkcs1::DecodeRsaPublicKey;
|
||||
use rsa::pkcs8::DecodePrivateKey;
|
||||
use rsa::pkcs8::DecodePublicKey;
|
||||
use rsa::signature::Verifier;
|
||||
use rsa::{
|
||||
sha2::{Sha256, Sha512},
|
||||
signature::Signer,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Formatter;
|
||||
use std::str::FromStr;
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
use strum::AsRefStr;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ApVerificationError {
|
||||
#[error("Signature algorithm and public key mismatch: {0} not compatible with {1}")]
|
||||
KeyAlgorithmMismatch(SigningAlgorithm, String),
|
||||
#[error("PKCS#1 v1.5 RSA signature verification failed: {0}")]
|
||||
RsaSignatureError(#[from] rsa::signature::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ApSigningError {
|
||||
#[error("Signature algorithm and public key mismatch: {0} not compatible with {1}")]
|
||||
KeyAlgorithmMismatch(SigningAlgorithm, String),
|
||||
#[error("Signing error: {0}")]
|
||||
RsaSigningError(#[from] rsa::signature::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum SigningAlgorithm {
|
||||
#[serde(rename = "hs2019")]
|
||||
Hs2019,
|
||||
#[serde(rename = "rsa-sha256")]
|
||||
RsaSha256,
|
||||
}
|
||||
|
||||
impl Display for SigningAlgorithm {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Hs2019 => write!(f, "hs2019"),
|
||||
Self::RsaSha256 => write!(f, "rsa-sha256"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, AsRefStr)]
|
||||
pub enum ApHttpVerificationKey<'a> {
|
||||
#[strum(serialize = "rsa-sha256")]
|
||||
RsaSha256(Cow<'a, rsa::pkcs1v15::VerifyingKey<Sha256>>),
|
||||
#[strum(serialize = "rsa-sha512")]
|
||||
RsaSha512(Cow<'a, rsa::pkcs1v15::VerifyingKey<Sha512>>),
|
||||
#[strum(serialize = "ed25519")]
|
||||
Ed25519(Cow<'a, ed25519_dalek::VerifyingKey>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, AsRefStr)]
|
||||
pub enum ApHttpPublicKey<'a> {
|
||||
#[strum(serialize = "rsa")]
|
||||
Rsa(Cow<'a, rsa::RsaPublicKey>),
|
||||
#[strum(serialize = "ed25519")]
|
||||
Ed25519(Cow<'a, ed25519_dalek::VerifyingKey>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Error)]
|
||||
#[error("Failed to parse the public key: No available parser could decode the PEM string")]
|
||||
pub struct ApHttpPublicKeyParseError;
|
||||
|
||||
impl FromStr for ApHttpPublicKey<'_> {
|
||||
type Err = ApHttpPublicKeyParseError;
|
||||
|
||||
fn from_str(input_pem: &str) -> Result<Self, Self::Err> {
|
||||
let pem = input_pem.trim();
|
||||
|
||||
let parse_pkcs1_rsa: &dyn Fn(_) -> _ = &|p| {
|
||||
Some(ApHttpPublicKey::Rsa(Cow::Owned(
|
||||
rsa::RsaPublicKey::from_pkcs1_pem(p).ok()?,
|
||||
)))
|
||||
};
|
||||
let parse_spki_rsa: &dyn Fn(_) -> _ = &|p| {
|
||||
Some(ApHttpPublicKey::Rsa(Cow::Owned(
|
||||
rsa::RsaPublicKey::from_public_key_pem(p).ok()?,
|
||||
)))
|
||||
};
|
||||
let parse_spki_ed25519: &dyn Fn(_) -> _ = &|p| {
|
||||
Some(ApHttpPublicKey::Ed25519(Cow::Owned(
|
||||
ed25519_dalek::VerifyingKey::from_public_key_pem(p).ok()?,
|
||||
)))
|
||||
};
|
||||
|
||||
// Some heuristics
|
||||
let parsers: &[_] = match pem {
|
||||
p if p.starts_with("-----BEGIN PUBLIC KEY-----") => {
|
||||
&[parse_spki_rsa, parse_spki_ed25519]
|
||||
}
|
||||
p if p.starts_with("-----BEGIN RSA PUBLIC KEY-----") => &[parse_pkcs1_rsa],
|
||||
_ => &[parse_spki_rsa, parse_spki_ed25519, parse_pkcs1_rsa],
|
||||
};
|
||||
|
||||
for parser in parsers {
|
||||
if let Some(k) = parser(pem) {
|
||||
return Ok(k);
|
||||
}
|
||||
}
|
||||
|
||||
Err(ApHttpPublicKeyParseError)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApHttpVerificationKey<'_> {
|
||||
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), ApVerificationError> {
|
||||
match self {
|
||||
ApHttpVerificationKey::RsaSha256(rsa_pubkey) => {
|
||||
let sig = rsa::pkcs1v15::Signature::try_from(signature)?;
|
||||
Ok(rsa_pubkey.verify(message, &sig)?)
|
||||
}
|
||||
ApHttpVerificationKey::RsaSha512(rsa_pubkey) => {
|
||||
let sig = rsa::pkcs1v15::Signature::try_from(signature)?;
|
||||
Ok(rsa_pubkey.verify(message, &sig)?)
|
||||
}
|
||||
ApHttpVerificationKey::Ed25519(ed25519_pubkey) => {
|
||||
let sig = ed25519_dalek::Signature::try_from(signature)?;
|
||||
Ok(ed25519_pubkey.verify_strict(message, &sig)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApHttpPublicKey<'_> {
|
||||
pub fn verify(
|
||||
&self,
|
||||
algorithm: SigningAlgorithm,
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
) -> Result<(), ApVerificationError> {
|
||||
match (self, algorithm) {
|
||||
(Self::Rsa(key), SigningAlgorithm::Hs2019) => {
|
||||
let verification_key = ApHttpVerificationKey::RsaSha256(Cow::Owned(
|
||||
rsa::pkcs1v15::VerifyingKey::new(key.clone().into_owned()),
|
||||
));
|
||||
|
||||
let Err(_) = verification_key.verify(message, signature) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let verification_key = ApHttpVerificationKey::RsaSha512(Cow::Owned(
|
||||
rsa::pkcs1v15::VerifyingKey::new(key.clone().into_owned()),
|
||||
));
|
||||
|
||||
Ok(verification_key.verify(message, signature)?)
|
||||
}
|
||||
(Self::Rsa(key), SigningAlgorithm::RsaSha256) => {
|
||||
let verification_key = ApHttpVerificationKey::RsaSha256(Cow::Owned(
|
||||
rsa::pkcs1v15::VerifyingKey::new(key.clone().into_owned()),
|
||||
));
|
||||
Ok(verification_key.verify(message, signature)?)
|
||||
}
|
||||
(_, SigningAlgorithm::RsaSha256) => Err(ApVerificationError::KeyAlgorithmMismatch(
|
||||
algorithm,
|
||||
self.as_ref().to_owned(),
|
||||
)),
|
||||
(Self::Ed25519(key), SigningAlgorithm::Hs2019) => {
|
||||
let verification_key = ApHttpVerificationKey::Ed25519(Cow::Borrowed(key.as_ref()));
|
||||
Ok(verification_key.verify(message, signature)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, AsRefStr)]
|
||||
pub enum ApHttpPrivateKey<'a> {
|
||||
#[strum(serialize = "rsa")]
|
||||
Rsa(Cow<'a, Box<rsa::RsaPrivateKey>>),
|
||||
#[strum(serialize = "ed25519")]
|
||||
Ed25519(Cow<'a, ed25519_dalek::SecretKey>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Error)]
|
||||
#[error("Failed to parse the private key: No available parser could decode the PEM string")]
|
||||
pub struct ApHttpPrivateKeyParseError;
|
||||
|
||||
impl FromStr for ApHttpPrivateKey<'_> {
|
||||
type Err = ApHttpPrivateKeyParseError;
|
||||
|
||||
fn from_str(input_pem: &str) -> Result<Self, Self::Err> {
|
||||
let pem = input_pem.trim();
|
||||
|
||||
let parse_pkcs1_rsa: &dyn Fn(_) -> _ = &|p| {
|
||||
Some(ApHttpPrivateKey::Rsa(Cow::Owned(Box::new(
|
||||
rsa::RsaPrivateKey::from_pkcs1_pem(p).ok()?,
|
||||
))))
|
||||
};
|
||||
let parse_pkcs8_rsa: &dyn Fn(_) -> _ = &|p| {
|
||||
Some(ApHttpPrivateKey::Rsa(Cow::Owned(Box::new(
|
||||
rsa::RsaPrivateKey::from_pkcs8_pem(p).ok()?,
|
||||
))))
|
||||
};
|
||||
let parse_pkcs8_ed25519: &dyn Fn(_) -> _ = &|p| {
|
||||
Some(ApHttpPrivateKey::Ed25519(Cow::Owned(
|
||||
ed25519_dalek::SigningKey::from_pkcs8_pem(p)
|
||||
.ok()?
|
||||
.to_bytes(),
|
||||
)))
|
||||
};
|
||||
|
||||
// Some heuristics
|
||||
let parsers: &[_] = match pem {
|
||||
p if p.contains("-----BEGIN PRIVATE KEY-----") => {
|
||||
&[parse_pkcs8_rsa, parse_pkcs8_ed25519]
|
||||
}
|
||||
p if p.contains("-----BEGIN RSA PRIVATE KEY-----") => &[parse_pkcs1_rsa],
|
||||
_ => &[parse_pkcs8_rsa, parse_pkcs8_ed25519, parse_pkcs1_rsa],
|
||||
};
|
||||
|
||||
for parser in parsers {
|
||||
if let Some(k) = parser(pem) {
|
||||
return Ok(k);
|
||||
}
|
||||
}
|
||||
|
||||
Err(ApHttpPrivateKeyParseError)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, AsRefStr)]
|
||||
pub enum ApHttpSigningKey<'a> {
|
||||
#[strum(serialize = "rsa-sha256")]
|
||||
RsaSha256(Cow<'a, rsa::pkcs1v15::SigningKey<Sha256>>),
|
||||
#[strum(serialize = "rsa-sha512")]
|
||||
RsaSha512(Cow<'a, rsa::pkcs1v15::SigningKey<Sha512>>),
|
||||
#[strum(serialize = "ed25519")]
|
||||
Ed25519(Cow<'a, ed25519_dalek::SigningKey>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ApSigningKey<'a> {
|
||||
pub key: ApHttpSigningKey<'a>,
|
||||
pub key_id: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> ApSigningKey<'a> {
|
||||
pub fn into_owned(self) -> ApSigningKey<'static> {
|
||||
ApSigningKey {
|
||||
key: self.key.into_owned(),
|
||||
key_id: Cow::Owned(self.key_id.into_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ApHttpSigningKey<'a> {
|
||||
pub fn into_owned(self) -> ApHttpSigningKey<'static> {
|
||||
match self {
|
||||
ApHttpSigningKey::RsaSha256(k) => ApHttpSigningKey::RsaSha256(Cow::Owned(k.into_owned())),
|
||||
ApHttpSigningKey::RsaSha512(k) => ApHttpSigningKey::RsaSha512(Cow::Owned(k.into_owned())),
|
||||
ApHttpSigningKey::Ed25519(k) => ApHttpSigningKey::Ed25519(Cow::Owned(k.into_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApHttpSigningKey<'_> {
|
||||
pub fn sign(
|
||||
&self,
|
||||
algorithm: SigningAlgorithm,
|
||||
message: &[u8],
|
||||
) -> Result<Vec<u8>, ApSigningError> {
|
||||
match (self, algorithm) {
|
||||
(Self::RsaSha256(key), SigningAlgorithm::RsaSha256 | SigningAlgorithm::Hs2019) => {
|
||||
Ok(Box::<[u8]>::from(key.sign(message)).into_vec())
|
||||
}
|
||||
(Self::RsaSha512(key), SigningAlgorithm::Hs2019) => {
|
||||
Ok(Box::<[u8]>::from(key.sign(message)).into_vec())
|
||||
}
|
||||
(Self::Ed25519(key), SigningAlgorithm::Hs2019) => {
|
||||
Ok(key.sign(message).to_bytes().to_vec())
|
||||
}
|
||||
(key, _) => Err(ApSigningError::KeyAlgorithmMismatch(
|
||||
algorithm,
|
||||
key.as_ref().to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign_base64(
|
||||
&self,
|
||||
algorithm: SigningAlgorithm,
|
||||
message: &[u8],
|
||||
) -> Result<String, ApSigningError> {
|
||||
let signed = self.sign(algorithm, message)?;
|
||||
use base64::prelude::*;
|
||||
Ok(BASE64_STANDARD.encode(signed))
|
||||
}
|
||||
}
|
||||
|
||||
impl ApHttpPrivateKey<'_> {
|
||||
pub fn create_signing_key<'a>(
|
||||
&'a self,
|
||||
key_id: &'a str,
|
||||
algorithm: SigningAlgorithm,
|
||||
) -> Result<ApSigningKey<'a>, ApSigningError> {
|
||||
Ok(ApSigningKey {
|
||||
key_id: Cow::Borrowed(key_id),
|
||||
key: match (self, algorithm) {
|
||||
(Self::Rsa(key), SigningAlgorithm::RsaSha256 | SigningAlgorithm::Hs2019) => {
|
||||
ApHttpSigningKey::RsaSha256(Cow::Owned(rsa::pkcs1v15::SigningKey::new(
|
||||
*key.as_ref().to_owned(),
|
||||
)))
|
||||
}
|
||||
(Self::Ed25519(key), SigningAlgorithm::Hs2019) => ApHttpSigningKey::Ed25519(
|
||||
Cow::Owned(ed25519_dalek::SigningKey::from_bytes(key.as_ref())),
|
||||
),
|
||||
(key, _) => {
|
||||
return Err(ApSigningError::KeyAlgorithmMismatch(
|
||||
algorithm,
|
||||
key.as_ref().to_owned(),
|
||||
))
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use indexmap::IndexSet;
|
||||
use magnetar_common::util::{FediverseTag, ValidName};
|
||||
use magnetar_host_meta::Xrd;
|
||||
use magnetar_webfinger::webfinger::WebFinger;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use strum::AsRefStr;
|
||||
use url::{Host, Url};
|
||||
|
||||
use crate::crypto::{ApSigningKey, SigningAlgorithm};
|
||||
|
||||
pub mod ap_client;
|
||||
pub mod client;
|
||||
pub mod crypto;
|
||||
pub mod lookup_flow;
|
||||
|
||||
/// The *visible* domain of fediverse handles, that gets resolved by WebFinger
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct HostUnmapped(Host);
|
||||
|
||||
impl AsRef<Host> for HostUnmapped {
|
||||
fn as_ref(&self) -> &Host {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// The real domain of fediverse handles used for federation
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct HostMapped(Host);
|
||||
|
||||
impl AsRef<Host> for HostMapped {
|
||||
fn as_ref(&self) -> &Host {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait HostMetaResolverService: Send + Sync {
|
||||
type Error;
|
||||
|
||||
async fn resolve(&self, host: &HostUnmapped) -> Result<Xrd, Self::Error>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait WebFingerResolverService: Send + Sync {
|
||||
type Error;
|
||||
|
||||
async fn resolve_url(&self, url: &str) -> Result<WebFinger, Self::Error>;
|
||||
|
||||
async fn resolve(
|
||||
&self,
|
||||
template_url: &str,
|
||||
resolved_uri: &str,
|
||||
) -> Result<WebFinger, Self::Error> {
|
||||
self.resolve_url(
|
||||
&template_url.replace(
|
||||
"{uri}",
|
||||
&percent_encoding::utf8_percent_encode(
|
||||
resolved_uri,
|
||||
percent_encoding::NON_ALPHANUMERIC,
|
||||
)
|
||||
.to_string(),
|
||||
),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnmappedUser {
|
||||
pub name: ValidName,
|
||||
pub host: HostUnmapped,
|
||||
}
|
||||
|
||||
impl From<&UnmappedUser> for FediverseTag {
|
||||
fn from(user: &UnmappedUser) -> Self {
|
||||
FediverseTag {
|
||||
name: user.name.clone(),
|
||||
host: Some(user.host.as_ref().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MappedUser {
|
||||
pub host_meta: Option<Xrd>,
|
||||
pub webfinger: WebFinger,
|
||||
pub tag_mapped: FediverseTag,
|
||||
pub ap_url: Url,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait FederationLookupService {
|
||||
type Error;
|
||||
|
||||
async fn map_fedi_tag(&self, user: &UnmappedUser) -> Result<MappedUser, Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash, AsRefStr)]
|
||||
pub enum ApSigningField {
|
||||
#[serde(rename = "(request-target)")]
|
||||
#[strum(serialize = "(request-target)")]
|
||||
PseudoRequestTarget,
|
||||
#[serde(rename = "(expires)")]
|
||||
#[strum(serialize = "(expires)")]
|
||||
PseudoExpires,
|
||||
#[serde(rename = "(created)")]
|
||||
#[strum(serialize = "(created)")]
|
||||
PseudoCreated,
|
||||
#[serde(rename = "date")]
|
||||
#[strum(serialize = "date")]
|
||||
Date,
|
||||
#[serde(rename = "host")]
|
||||
#[strum(serialize = "host")]
|
||||
Host,
|
||||
#[serde(rename = "digest")]
|
||||
#[strum(serialize = "digest")]
|
||||
Digest,
|
||||
#[serde(rename = "content-length")]
|
||||
#[strum(serialize = "content-length")]
|
||||
ContentLength,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApSignature {
|
||||
pub key_id: String,
|
||||
pub algorithm: Option<SigningAlgorithm>,
|
||||
pub created: Option<DateTime<Utc>>,
|
||||
pub expires: Option<DateTime<Utc>>,
|
||||
pub headers: Option<ApSigningHeaders>,
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApSigningHeaders(pub(crate) IndexSet<ApSigningField>);
|
||||
|
||||
pub trait SigningParts: Send {
|
||||
fn get_created(&self) -> Option<&DateTime<Utc>>;
|
||||
fn get_expires(&self) -> Option<&DateTime<Utc>>;
|
||||
}
|
||||
|
||||
pub trait SigningInput: SigningParts + Sync {
|
||||
fn create_signing_input(&self) -> Vec<(ApSigningField, String)>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ApClientService: Send + Sync {
|
||||
type Error;
|
||||
|
||||
async fn sign_request(
|
||||
&self,
|
||||
signing_key: ApSigningKey<'_>,
|
||||
signing_algorithm: SigningAlgorithm,
|
||||
request: &dyn SigningInput,
|
||||
) -> Result<ApSignature, Self::Error>;
|
||||
|
||||
async fn signed_get(
|
||||
&self,
|
||||
signing_key: ApSigningKey<'_>,
|
||||
signing_algorithm: SigningAlgorithm,
|
||||
expires: Option<chrono::DateTime<Utc>>,
|
||||
url: &str,
|
||||
) -> Result<Value, Self::Error>;
|
||||
|
||||
async fn signed_post(
|
||||
&self,
|
||||
signing_key: ApSigningKey<'_>,
|
||||
signing_algorithm: SigningAlgorithm,
|
||||
expires: Option<chrono::DateTime<Utc>>,
|
||||
url: &str,
|
||||
body: &Value,
|
||||
) -> Result<String, Self::Error>;
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
use std::{io::Cursor, sync::Arc};
|
||||
|
||||
use magnetar_common::{
|
||||
config::MagnetarNetworkingProtocol,
|
||||
util::{FediverseTag, FediverseTagDisplay, FediverseTagParseError},
|
||||
};
|
||||
use magnetar_core::web_model::{acct::Acct, content_type::ContentJrdJson};
|
||||
use magnetar_host_meta::{Xrd, XrdXml};
|
||||
use magnetar_webfinger::webfinger::{WebFinger, WebFingerRel, WebFingerSubject};
|
||||
use thiserror::Error;
|
||||
use tracing::trace;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
client::federation_client::{FederationClient, FederationClientError},
|
||||
FederationLookupService, HostMetaResolverService, HostUnmapped, MappedUser, UnmappedUser,
|
||||
WebFingerResolverService,
|
||||
};
|
||||
|
||||
pub struct HostMetaResolverProviderDefault {
|
||||
client: Arc<FederationClient>,
|
||||
protocol: MagnetarNetworkingProtocol,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl HostMetaResolverService for HostMetaResolverProviderDefault {
|
||||
type Error = FederationClientError;
|
||||
|
||||
async fn resolve(&self, HostUnmapped(host): &HostUnmapped) -> Result<Xrd, Self::Error> {
|
||||
let host_meta_xml = self
|
||||
.client
|
||||
.get(Url::parse(&format!(
|
||||
"{}://{}/.well-known/host-meta",
|
||||
self.protocol.as_ref(),
|
||||
host
|
||||
))?)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let XrdXml::Xrd(xrd) = quick_xml::de::from_reader(Cursor::new(host_meta_xml))?;
|
||||
|
||||
Ok(xrd)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebFingerResolverProviderDefault {
|
||||
client: Arc<FederationClient>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl WebFingerResolverService for WebFingerResolverProviderDefault {
|
||||
type Error = FederationClientError;
|
||||
|
||||
async fn resolve_url(&self, url: &str) -> Result<WebFinger, Self::Error> {
|
||||
let host_meta_xml = self
|
||||
.client
|
||||
.get(Url::parse(url)?)
|
||||
.content_type(ContentJrdJson)
|
||||
.send()
|
||||
.await?;
|
||||
let webfinger = serde_json::from_reader(Cursor::new(host_meta_xml))?;
|
||||
Ok(webfinger)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FederationLookupErrror {
|
||||
#[error("Federation client error: {0}")]
|
||||
FederationClientError(#[from] FederationClientError),
|
||||
#[error("Fediverse tag parse error: {0}")]
|
||||
FediverseTagParseError(#[from] FediverseTagParseError),
|
||||
#[error("URL parse error: {0}")]
|
||||
UrlParseError(#[from] url::ParseError),
|
||||
#[error("Missing ActivityStreams URL in WebFinger")]
|
||||
MissingApUrl,
|
||||
#[error("Missing Acct URI in WebFinger")]
|
||||
MissingAcctUri,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FederationLookupServiceProviderDefault {
|
||||
host_meta_resolver: Arc<dyn HostMetaResolverService<Error = FederationClientError>>,
|
||||
webfinger_resolver: Arc<dyn WebFingerResolverService<Error = FederationClientError>>,
|
||||
protocol: MagnetarNetworkingProtocol,
|
||||
}
|
||||
|
||||
impl FederationLookupServiceProviderDefault {
|
||||
pub fn new(
|
||||
host_meta_resolver: Arc<dyn HostMetaResolverService<Error = FederationClientError>>,
|
||||
webfinger_resolver: Arc<dyn WebFingerResolverService<Error = FederationClientError>>,
|
||||
protocol: MagnetarNetworkingProtocol,
|
||||
) -> Self {
|
||||
Self {
|
||||
host_meta_resolver,
|
||||
webfinger_resolver,
|
||||
protocol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl FederationLookupService for FederationLookupServiceProviderDefault {
|
||||
type Error = FederationLookupErrror;
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
async fn map_fedi_tag(
|
||||
&self,
|
||||
user: &UnmappedUser,
|
||||
) -> Result<MappedUser, FederationLookupErrror> {
|
||||
trace!("Fetching flow initiated");
|
||||
let host_meta = self.host_meta_resolver.resolve(&user.host).await;
|
||||
let webfinger_template = match &host_meta {
|
||||
Ok(h) => {
|
||||
trace!("host-meta found: {:?}", h);
|
||||
h.get_webfinger_template().map(str::to_owned)
|
||||
}
|
||||
Err(e) => {
|
||||
trace!("host-meta fetch failed: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
Xrd::default_host_meta(self.protocol.as_ref(), &user.host.as_ref().to_string())
|
||||
.get_webfinger_template()
|
||||
.expect("default WebFinger template")
|
||||
.to_owned()
|
||||
});
|
||||
|
||||
let webfinger = self
|
||||
.webfinger_resolver
|
||||
.resolve(
|
||||
&webfinger_template,
|
||||
Acct::from(FediverseTag::from(user)).as_ref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
trace!("Webfinger fetched: {:?}", webfinger);
|
||||
|
||||
let real_tag = match &webfinger.subject {
|
||||
WebFingerSubject::Acct(acct) => Some(acct.clone()),
|
||||
_ => webfinger
|
||||
.aliases
|
||||
.iter()
|
||||
.flatten()
|
||||
.find_map(|alias| match alias {
|
||||
WebFingerSubject::Acct(acct) => Some(acct.clone()),
|
||||
_ => None,
|
||||
}),
|
||||
}
|
||||
.ok_or(FederationLookupErrror::MissingAcctUri)?;
|
||||
|
||||
let ap_url = webfinger
|
||||
.links
|
||||
.iter()
|
||||
.find_map(|link| match link {
|
||||
WebFingerRel::RelSelf { href, .. } | WebFingerRel::RelSelfAlt { href, .. } => {
|
||||
Some(href)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.ok_or(FederationLookupErrror::MissingApUrl)?
|
||||
.parse()?;
|
||||
|
||||
Ok(MappedUser {
|
||||
host_meta: host_meta.ok(),
|
||||
webfinger,
|
||||
tag_mapped: FediverseTag::try_from(&FediverseTagDisplay::try_from(&real_tag)?)?,
|
||||
ap_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
||||
use headers::UserAgent;
|
||||
use magnetar_common::{config::MagnetarNetworkingProtocol, util::FediverseTag};
|
||||
use tracing::{info, Level};
|
||||
|
||||
use crate::{
|
||||
client::federation_client::FederationClient, FederationLookupService, HostUnmapped,
|
||||
UnmappedUser,
|
||||
};
|
||||
|
||||
use super::{
|
||||
FederationLookupServiceProviderDefault, HostMetaResolverProviderDefault,
|
||||
WebFingerResolverProviderDefault,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn should_resolve() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::TRACE)
|
||||
.init();
|
||||
|
||||
let client = Arc::new(
|
||||
FederationClient::new(true, 64000, 20, UserAgent::from_static("magnetar/0.42"))
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let federation_lookup = FederationLookupServiceProviderDefault::new(
|
||||
Arc::new(HostMetaResolverProviderDefault {
|
||||
protocol: MagnetarNetworkingProtocol::Https,
|
||||
client: client.clone(),
|
||||
}),
|
||||
Arc::new(WebFingerResolverProviderDefault { client }),
|
||||
MagnetarNetworkingProtocol::Https,
|
||||
);
|
||||
let tag: FediverseTag = "@natty@astolfo.social".parse().unwrap();
|
||||
info!("Resolving: {}", tag);
|
||||
let resolved = federation_lookup
|
||||
.map_fedi_tag(&UnmappedUser {
|
||||
name: tag.name,
|
||||
host: HostUnmapped(tag.host.unwrap()),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
info!("Resolved: {:#?}", resolved);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "magnetar_host_meta"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
magnetar_core = { path = "../core" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] }
|
|
@ -0,0 +1,97 @@
|
|||
use magnetar_core::web_model::{content_type::ContentXrdXml, rel::RelLrdd, Rel};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum XrdXml {
|
||||
#[serde(rename = "XRD")]
|
||||
Xrd(Xrd),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Xrd {
|
||||
#[serde(rename = "@xmlns")]
|
||||
xmlns: String,
|
||||
#[serde(rename = "Link")]
|
||||
links: Vec<Link>,
|
||||
}
|
||||
|
||||
impl Xrd {
|
||||
pub fn default_host_meta(protocol: &str, domain: &str) -> Xrd {
|
||||
Xrd {
|
||||
xmlns: r#"http://docs.oasis-open.org/ns/xri/xrd-1.0"#.to_string(),
|
||||
links: vec![Link {
|
||||
rel: Some(RelLrdd.rel().to_string()),
|
||||
r#type: Some(ContentXrdXml.as_ref().to_string()),
|
||||
href: None,
|
||||
template: Some(format!(
|
||||
"{}://{}/.well-known/webfinger?resource={{uri}}",
|
||||
protocol, domain
|
||||
)),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_webfinger_template(&self) -> Option<&str> {
|
||||
self.links
|
||||
.iter()
|
||||
.find(|l| {
|
||||
l.r#type.as_deref() == Some(ContentXrdXml.as_ref())
|
||||
&& l.rel.as_deref() == Some(RelLrdd.as_ref())
|
||||
})
|
||||
.and_then(|l| l.template.as_deref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename = "Link")]
|
||||
pub struct Link {
|
||||
#[serde(rename = "@rel")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
rel: Option<String>,
|
||||
#[serde(rename = "@type")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
r#type: Option<String>,
|
||||
#[serde(rename = "@href")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
href: Option<String>,
|
||||
#[serde(rename = "@template")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
template: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::error::Error;
|
||||
|
||||
use crate::{Xrd, XrdXml};
|
||||
|
||||
#[test]
|
||||
fn should_parse_host_meta() -> Result<(), Box<dyn Error>> {
|
||||
let xml = r#"
|
||||
<?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="https://example.org/.well-known/webfinger?resource={uri}"/>
|
||||
</XRD>
|
||||
"#;
|
||||
|
||||
let XrdXml::Xrd(xrd) = quick_xml::de::from_str::<XrdXml>(xml)?;
|
||||
|
||||
assert_eq!(xrd, Xrd::default_host_meta("https", "example.org"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_invalid_root_tag() {
|
||||
let xml = r#"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ABC xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" type="application/xrd+xml" template="https://example.org/.well-known/webfinger?resource={uri}"/>
|
||||
</ABC>
|
||||
"#;
|
||||
|
||||
let xrd = quick_xml::de::from_str::<XrdXml>(xml);
|
||||
|
||||
assert!(xrd.is_err());
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "magnetar_calckey_model"
|
||||
name = "magnetar_model"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
|
@ -8,20 +8,26 @@ crate-type = ["rlib"]
|
|||
|
||||
[dependencies]
|
||||
ck = { path = "./entity_ck" }
|
||||
ext_calckey_model_migration = { path = "./migration" }
|
||||
ext_model_migration = { path = "./migration" }
|
||||
|
||||
magnetar_common = { path = "../magnetar_common" }
|
||||
magnetar_sdk = { path = "../magnetar_sdk" }
|
||||
|
||||
dotenvy = { workspace = true}
|
||||
dotenvy = { workspace = true }
|
||||
futures-core = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-util = { workspace = true}
|
||||
redis = { workspace = true, features = ["tokio-comp", "json", "serde_json"]}
|
||||
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls", "macros"] }
|
||||
tokio-util = { workspace = true }
|
||||
redis = { workspace = true, features = ["tokio-comp", "json", "serde_json"] }
|
||||
sea-orm = { workspace = true, features = [
|
||||
"sqlx-postgres",
|
||||
"runtime-tokio-rustls",
|
||||
"macros",
|
||||
] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::AntennaSrcEnum;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
@ -27,8 +27,6 @@ pub struct Model {
|
|||
pub case_sensitive: bool,
|
||||
#[sea_orm(column_name = "withReplies")]
|
||||
pub with_replies: bool,
|
||||
#[sea_orm(column_name = "userGroupJoiningId")]
|
||||
pub user_group_joining_id: Option<String>,
|
||||
pub users: Vec<String>,
|
||||
#[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")]
|
||||
pub exclude_keywords: Json,
|
||||
|
@ -48,14 +46,6 @@ pub enum Relation {
|
|||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user_group_joining::Entity",
|
||||
from = "Column::UserGroupJoiningId",
|
||||
to = "super::user_group_joining::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
UserGroupJoining,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user_list::Entity",
|
||||
from = "Column::UserListId",
|
||||
|
@ -78,12 +68,6 @@ impl Related<super::user::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_joining::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupJoining.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_list::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserList.def()
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -65,8 +65,6 @@ pub enum Relation {
|
|||
on_delete = "SetNull"
|
||||
)]
|
||||
DriveFolder,
|
||||
#[sea_orm(has_many = "super::page::Entity")]
|
||||
Page,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
|
@ -83,12 +81,6 @@ impl Related<super::drive_folder::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::page::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Page.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::MetaSensitivemediadetectionEnum;
|
||||
use super::sea_orm_active_enums::MetaSensitivemediadetectionsensitivityEnum;
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
|
@ -20,8 +20,6 @@ pub mod drive_folder;
|
|||
pub mod emoji;
|
||||
pub mod follow_request;
|
||||
pub mod following;
|
||||
pub mod gallery_like;
|
||||
pub mod gallery_post;
|
||||
pub mod hashtag;
|
||||
pub mod instance;
|
||||
pub mod meta;
|
||||
|
@ -36,8 +34,6 @@ pub mod note_thread_muting;
|
|||
pub mod note_unread;
|
||||
pub mod note_watching;
|
||||
pub mod notification;
|
||||
pub mod page;
|
||||
pub mod page_like;
|
||||
pub mod password_reset_request;
|
||||
pub mod poll;
|
||||
pub mod poll_vote;
|
||||
|
@ -52,10 +48,6 @@ pub mod signin;
|
|||
pub mod sw_subscription;
|
||||
pub mod used_username;
|
||||
pub mod user;
|
||||
pub mod user_group;
|
||||
pub mod user_group_invitation;
|
||||
pub mod user_group_invite;
|
||||
pub mod user_group_joining;
|
||||
pub mod user_ip;
|
||||
pub mod user_keypair;
|
||||
pub mod user_list;
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::MutedNoteReasonEnum;
|
||||
use sea_orm::entity::prelude::*;
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::NoteVisibilityEnum;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
@ -60,6 +60,8 @@ pub struct Model {
|
|||
pub thread_id: Option<String>,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: Option<DateTimeWithTimeZone>,
|
||||
pub is_quote: Option<bool>,
|
||||
pub is_renote: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::NotificationTypeEnum;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
@ -24,8 +24,6 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "followRequestId")]
|
||||
pub follow_request_id: Option<String>,
|
||||
pub r#type: NotificationTypeEnum,
|
||||
#[sea_orm(column_name = "userGroupInvitationId")]
|
||||
pub user_group_invitation_id: Option<String>,
|
||||
#[sea_orm(column_name = "customBody")]
|
||||
pub custom_body: Option<String>,
|
||||
#[sea_orm(column_name = "customHeader")]
|
||||
|
@ -78,14 +76,6 @@ pub enum Relation {
|
|||
on_delete = "Cascade"
|
||||
)]
|
||||
User1,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user_group_invitation::Entity",
|
||||
from = "Column::UserGroupInvitationId",
|
||||
to = "super::user_group_invitation::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
UserGroupInvitation,
|
||||
}
|
||||
|
||||
impl Related<super::access_token::Entity> for Entity {
|
||||
|
@ -106,10 +96,4 @@ impl Related<super::note::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_invitation::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupInvitation.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::PollNotevisibilityEnum;
|
||||
use sea_orm::entity::prelude::*;
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
pub use super::abuse_user_report::Entity as AbuseUserReport;
|
||||
pub use super::access_token::Entity as AccessToken;
|
||||
|
@ -18,8 +18,6 @@ pub use super::drive_folder::Entity as DriveFolder;
|
|||
pub use super::emoji::Entity as Emoji;
|
||||
pub use super::follow_request::Entity as FollowRequest;
|
||||
pub use super::following::Entity as Following;
|
||||
pub use super::gallery_like::Entity as GalleryLike;
|
||||
pub use super::gallery_post::Entity as GalleryPost;
|
||||
pub use super::hashtag::Entity as Hashtag;
|
||||
pub use super::instance::Entity as Instance;
|
||||
pub use super::meta::Entity as Meta;
|
||||
|
@ -34,8 +32,6 @@ pub use super::note_thread_muting::Entity as NoteThreadMuting;
|
|||
pub use super::note_unread::Entity as NoteUnread;
|
||||
pub use super::note_watching::Entity as NoteWatching;
|
||||
pub use super::notification::Entity as Notification;
|
||||
pub use super::page::Entity as Page;
|
||||
pub use super::page_like::Entity as PageLike;
|
||||
pub use super::password_reset_request::Entity as PasswordResetRequest;
|
||||
pub use super::poll::Entity as Poll;
|
||||
pub use super::poll_vote::Entity as PollVote;
|
||||
|
@ -49,10 +45,6 @@ pub use super::signin::Entity as Signin;
|
|||
pub use super::sw_subscription::Entity as SwSubscription;
|
||||
pub use super::used_username::Entity as UsedUsername;
|
||||
pub use super::user::Entity as User;
|
||||
pub use super::user_group::Entity as UserGroup;
|
||||
pub use super::user_group_invitation::Entity as UserGroupInvitation;
|
||||
pub use super::user_group_invite::Entity as UserGroupInvite;
|
||||
pub use super::user_group_joining::Entity as UserGroupJoining;
|
||||
pub use super::user_ip::Entity as UserIp;
|
||||
pub use super::user_keypair::Entity as UserKeypair;
|
||||
pub use super::user_list::Entity as UserList;
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use super::sea_orm_active_enums::RelayStatusEnum;
|
||||
use sea_orm::entity::prelude::*;
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -8,8 +8,6 @@ use serde::{Deserialize, Serialize};
|
|||
pub enum AntennaSrcEnum {
|
||||
#[sea_orm(string_value = "all")]
|
||||
All,
|
||||
#[sea_orm(string_value = "group")]
|
||||
Group,
|
||||
#[sea_orm(string_value = "home")]
|
||||
Home,
|
||||
#[sea_orm(string_value = "instances")]
|
||||
|
@ -100,14 +98,10 @@ pub enum NotificationTypeEnum {
|
|||
Follow,
|
||||
#[sea_orm(string_value = "followRequestAccepted")]
|
||||
FollowRequestAccepted,
|
||||
#[sea_orm(string_value = "groupInvited")]
|
||||
GroupInvited,
|
||||
#[sea_orm(string_value = "mention")]
|
||||
Mention,
|
||||
#[sea_orm(string_value = "pollEnded")]
|
||||
PollEnded,
|
||||
#[sea_orm(string_value = "pollVote")]
|
||||
PollVote,
|
||||
#[sea_orm(string_value = "quote")]
|
||||
Quote,
|
||||
#[sea_orm(string_value = "reaction")]
|
||||
|
@ -120,20 +114,6 @@ pub enum NotificationTypeEnum {
|
|||
Reply,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
enum_name = "page_visibility_enum"
|
||||
)]
|
||||
pub enum PageVisibilityEnum {
|
||||
#[sea_orm(string_value = "followers")]
|
||||
Followers,
|
||||
#[sea_orm(string_value = "public")]
|
||||
Public,
|
||||
#[sea_orm(string_value = "specified")]
|
||||
Specified,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -173,35 +153,3 @@ pub enum UserProfileFfvisibilityEnum {
|
|||
#[sea_orm(string_value = "public")]
|
||||
Public,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
enum_name = "user_profile_mutingnotificationtypes_enum"
|
||||
)]
|
||||
pub enum UserProfileMutingnotificationtypesEnum {
|
||||
#[sea_orm(string_value = "app")]
|
||||
App,
|
||||
#[sea_orm(string_value = "follow")]
|
||||
Follow,
|
||||
#[sea_orm(string_value = "followRequestAccepted")]
|
||||
FollowRequestAccepted,
|
||||
#[sea_orm(string_value = "groupInvited")]
|
||||
GroupInvited,
|
||||
#[sea_orm(string_value = "mention")]
|
||||
Mention,
|
||||
#[sea_orm(string_value = "pollEnded")]
|
||||
PollEnded,
|
||||
#[sea_orm(string_value = "pollVote")]
|
||||
PollVote,
|
||||
#[sea_orm(string_value = "quote")]
|
||||
Quote,
|
||||
#[sea_orm(string_value = "reaction")]
|
||||
Reaction,
|
||||
#[sea_orm(string_value = "receiveFollowRequest")]
|
||||
ReceiveFollowRequest,
|
||||
#[sea_orm(string_value = "renote")]
|
||||
Renote,
|
||||
#[sea_orm(string_value = "reply")]
|
||||
Reply,
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -106,10 +106,6 @@ pub enum Relation {
|
|||
DriveFile1,
|
||||
#[sea_orm(has_many = "super::drive_folder::Entity")]
|
||||
DriveFolder,
|
||||
#[sea_orm(has_many = "super::gallery_like::Entity")]
|
||||
GalleryLike,
|
||||
#[sea_orm(has_many = "super::gallery_post::Entity")]
|
||||
GalleryPost,
|
||||
#[sea_orm(has_many = "super::meta::Entity")]
|
||||
Meta,
|
||||
#[sea_orm(has_many = "super::moderation_log::Entity")]
|
||||
|
@ -128,10 +124,6 @@ pub enum Relation {
|
|||
NoteUnread,
|
||||
#[sea_orm(has_many = "super::note_watching::Entity")]
|
||||
NoteWatching,
|
||||
#[sea_orm(has_many = "super::page::Entity")]
|
||||
Page,
|
||||
#[sea_orm(has_many = "super::page_like::Entity")]
|
||||
PageLike,
|
||||
#[sea_orm(has_many = "super::password_reset_request::Entity")]
|
||||
PasswordResetRequest,
|
||||
#[sea_orm(has_many = "super::poll_vote::Entity")]
|
||||
|
@ -144,14 +136,6 @@ pub enum Relation {
|
|||
Signin,
|
||||
#[sea_orm(has_many = "super::sw_subscription::Entity")]
|
||||
SwSubscription,
|
||||
#[sea_orm(has_many = "super::user_group::Entity")]
|
||||
UserGroup,
|
||||
#[sea_orm(has_many = "super::user_group_invitation::Entity")]
|
||||
UserGroupInvitation,
|
||||
#[sea_orm(has_many = "super::user_group_invite::Entity")]
|
||||
UserGroupInvite,
|
||||
#[sea_orm(has_many = "super::user_group_joining::Entity")]
|
||||
UserGroupJoining,
|
||||
#[sea_orm(has_one = "super::user_keypair::Entity")]
|
||||
UserKeypair,
|
||||
#[sea_orm(has_many = "super::user_list::Entity")]
|
||||
|
@ -218,18 +202,6 @@ impl Related<super::drive_folder::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::gallery_like::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::GalleryLike.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::gallery_post::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::GalleryPost.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::meta::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Meta.def()
|
||||
|
@ -284,18 +256,6 @@ impl Related<super::note_watching::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::page::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Page.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::page_like::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::PageLike.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::password_reset_request::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::PasswordResetRequest.def()
|
||||
|
@ -332,30 +292,6 @@ impl Related<super::sw_subscription::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroup.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_invitation::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupInvitation.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_invite::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupInvite.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_group_joining::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserGroupJoining.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user_keypair::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::UserKeypair.def()
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.1
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue