Compare commits
249 Commits
activity-p
...
main
Author | SHA1 | Date |
---|---|---|
|
657fa1f5bb | |
|
221aa69a18 | |
|
277dcb5e3e | |
|
dab7585a8b | |
|
647006d04f | |
|
3e4ae86c38 | |
|
82945279de | |
|
5363a0c137 | |
|
5666bb4622 | |
|
8aa2a4dac4 | |
|
ff6458ea2e | |
|
ad253fb02b | |
|
11ff0ad2c0 | |
|
0a23953ae9 | |
|
2acc41587a | |
|
56f80f290a | |
|
d598387795 | |
|
52cced9537 | |
|
c10594bf7e | |
|
369df105c9 | |
|
e53afb3313 | |
|
ad3b0e1ffb | |
|
51375e2ded | |
|
531b10ef85 | |
|
845fcb385b | |
|
e4beba0aea | |
|
161996632b | |
|
5c568b1657 | |
|
5fb85e0db6 | |
|
80c5bf8ae6 | |
|
b9160305f1 | |
|
9c42b20fa9 | |
|
69c126d860 | |
|
7581ecf331 | |
|
766fd8ea7d | |
|
5241b18b0d | |
|
88df8eca55 | |
|
62fc36ff03 | |
|
611afc591c | |
|
09f564746f | |
|
607cb2ded2 | |
|
aebc9803db | |
|
cb9bf082f4 | |
|
87e9fb36e1 | |
|
1173360265 | |
|
78c93f3c20 | |
|
b74f2d69a4 | |
|
e06906dd6e | |
|
155e458806 | |
|
ea4751400b | |
|
7c44e55597 | |
|
6c7e3d79e0 | |
|
bca16253af | |
|
1f10156ebb | |
|
aeb94687b5 | |
|
553dbb9b7b | |
|
e21c5a8132 | |
|
8b6bdd6e02 | |
|
b5812de552 | |
|
4f4a871ae5 | |
|
e1fd078367 | |
|
ce46782318 | |
|
aefef079a7 | |
|
156ad5c499 | |
|
13e5d447e8 | |
|
db444adcc1 | |
|
ff3f0927fb | |
|
d12b65fff7 | |
|
812d4ee6b2 | |
|
8618b2084d | |
|
014b394f66 | |
|
0dff0c0785 | |
|
ffed556107 | |
|
5aceca72bd | |
|
a640890cad | |
|
799730bedc | |
|
b295c66f0c | |
|
66ce0de3fa | |
|
236502ad05 | |
|
cbf6c7fe43 | |
|
ea0d681e5c | |
|
dc86b4e67d | |
|
7f0be08799 | |
|
1394557f81 | |
|
459f944243 | |
|
a169ebb004 | |
|
7c2909bceb | |
|
8d9415b3ab | |
|
d64d34b2ef | |
|
169e85dead | |
|
16739635d1 | |
|
7bfb5c0f91 | |
|
c8d658f807 | |
|
5430fc81f4 | |
|
f441de806f | |
|
7b02f84271 | |
|
ad3528055f | |
|
98fb2ef0d8 | |
|
94cff7c2c8 | |
|
2e4903e603 | |
|
b9f0f33b3c | |
|
185262eb05 | |
|
475c33e57e | |
|
3c16574d03 | |
|
a51e412cb8 | |
|
ff00dfebb6 | |
|
364074aecc | |
|
d16f9d704d | |
|
ce1bfc0ee1 | |
|
4dd3cd59ac | |
|
13f5ad3672 | |
|
789852211b | |
|
fdf9966ef6 | |
|
4834daceec | |
|
80a29f771f | |
|
2fea3b79a7 | |
|
22208d4c68 | |
|
b2a55f1d6d | |
|
1366adcebf | |
|
f06c74c85c | |
|
b2543b29d3 | |
|
1bef42ead5 | |
|
a658452138 | |
|
3b1eab8069 | |
|
811fecf99a | |
|
c6d84d6287 | |
|
024cdef68c | |
|
deb7f6ef5e | |
|
074c6f999e | |
|
c2cfd7e007 | |
|
9f633d0749 | |
|
bbc4f84ceb | |
|
152c4e6fc6 | |
|
93caa314c9 | |
|
15b0ea8e26 | |
|
42bf912bb9 | |
|
ab18633fbc | |
|
6003f5e404 | |
|
6b96f60b43 | |
|
ce8cb5aad0 | |
|
9c8489ced7 | |
|
64350cfcb8 | |
|
b50249da02 | |
|
7fbcc4ec19 | |
|
05469f68a8 | |
|
69cb1c5220 | |
|
76c4d0267f | |
|
7ad46191ec | |
|
bf2d475f78 | |
|
72ed3391fc | |
|
10bddd886e | |
|
c3e7791b3e | |
|
f23aae4ac7 | |
|
cb8e42d219 | |
|
f55ff2b761 | |
|
73fa4d2884 | |
|
e2cab2aa9b | |
|
79660fb816 | |
|
81d0c678d8 | |
|
d1ca62a807 | |
|
10256c683c | |
|
7eb03d59fb | |
|
4f6e6163cc | |
|
e1859c98bd | |
|
3b8997d153 | |
|
2173ee452f | |
|
0ed23fe68e | |
|
0bb0c775dc | |
|
4cb7431681 | |
|
e53e274d1d | |
|
cb645ef0f6 | |
|
b4611bda78 | |
|
0ed06ee68f | |
|
3efc376f2b | |
|
7eed5d6b62 | |
|
b801ab83ac | |
|
fdfd3163b1 | |
|
e7812a816b | |
|
0ad015cd68 | |
|
6908a2f350 | |
|
a5ab2acca0 | |
|
c8627a996c | |
|
2c3d675392 | |
|
5a8dc04915 | |
|
a34829c63d | |
|
cf04146d2f | |
|
5b9b813037 | |
|
970644ffc7 | |
|
8e02e46be5 | |
|
f4fa5925f7 | |
|
734ace5d05 | |
|
97407097b7 | |
|
7dc38ada9a | |
|
acdc3e8bc1 | |
|
0755dac002 | |
|
4bbc368f92 | |
|
3cd43d840a | |
|
771795d81f | |
|
f0e56deca9 | |
|
5572695515 | |
|
18d526cf8c | |
|
fc86f0e29c | |
|
42e68fffcd | |
|
f34be3a104 | |
|
216f4229fc | |
|
c6a282c26e | |
|
91883c6b36 | |
|
6ed6066b1f | |
|
146c072c92 | |
|
c4a8ebebf3 | |
|
42fa83c6e2 | |
|
86d5c87e9a | |
|
23a63f2fe9 | |
|
68abd4f787 | |
|
8ee5ee8bc0 | |
|
03be3d02e3 | |
|
38753bab28 | |
|
d0d977e6eb | |
|
26bd6fe4b2 | |
|
c4fd99fa45 | |
|
d2bc679740 | |
|
154cc27c07 | |
|
95bce443be | |
|
703e1191c2 | |
|
453891ddf4 | |
|
c45ec852dd | |
|
4431a3ad62 | |
|
a6ee6bfbde | |
|
7c8e65f556 | |
|
8009546bfe | |
|
52dc491a47 | |
|
9b26691ff4 | |
|
24d44632e0 | |
|
46e0766a36 | |
|
1af8f4e213 | |
|
95141388fa | |
|
a8636947b9 | |
|
be08be61c6 | |
|
c58bc2994d | |
|
fc0ec19afb | |
|
a0bdab23e7 | |
|
b4e01fee63 | |
|
36c3507d84 | |
|
2525b3a50a | |
|
3837980e50 | |
|
5c8db9b243 | |
|
98c5bcb4c4 | |
|
df275de905 | |
|
dde74dd56b |
|
@ -1,2 +1,2 @@
|
||||||
[registries.crates-io]
|
[registries.crates-io]
|
||||||
protocol = "sparse"
|
protocol = "sparse"
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
nattyarch.local {
|
magnetar-dev.local {
|
||||||
log {
|
log {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle /.well-known/host-meta {
|
||||||
|
reverse_proxy 127.0.0.1:4939
|
||||||
|
}
|
||||||
|
|
||||||
handle /.well-known/webfinger {
|
handle /.well-known/webfinger {
|
||||||
reverse_proxy 127.0.0.1:4939
|
reverse_proxy 127.0.0.1:4939
|
||||||
|
@ -20,7 +24,7 @@ nattyarch.local {
|
||||||
}
|
}
|
||||||
|
|
||||||
@render_html {
|
@render_html {
|
||||||
not path /api* /proxy* /files* /avatar* /identicon* /streaming
|
not path /api* /proxy* /files* /avatar* /identicon*
|
||||||
header Accept text/html*
|
header Accept text/html*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
pipeline:
|
steps:
|
||||||
publish-docker-latest:
|
publish-docker-latest:
|
||||||
image: docker.io/plugins/kaniko
|
image: docker.io/plugins/kaniko
|
||||||
settings:
|
settings:
|
||||||
|
@ -12,7 +12,5 @@ pipeline:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
|
|
||||||
when:
|
when:
|
||||||
event: [push]
|
- event: push
|
||||||
branch: [main]
|
branch: main
|
||||||
|
|
||||||
branches: [main]
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
106
Cargo.toml
106
Cargo.toml
|
@ -8,101 +8,143 @@ license = "AGPL-3.0-only"
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
".",
|
".",
|
||||||
"ext_activity_pub",
|
"ext_activity_streams",
|
||||||
|
"ext_federation",
|
||||||
"ext_nodeinfo",
|
"ext_nodeinfo",
|
||||||
"ext_webfinger",
|
"ext_webfinger",
|
||||||
"ext_calckey_model",
|
"ext_model",
|
||||||
"fe_calckey",
|
"fe_calckey",
|
||||||
"magnetar_common",
|
"magnetar_common",
|
||||||
|
"magnetar_runtime",
|
||||||
"magnetar_sdk",
|
"magnetar_sdk",
|
||||||
"core"
|
"magnetar_mmm_parser",
|
||||||
|
"core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.0"
|
version = "0.3.0-alpha"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
axum = "0.6"
|
async-stream = "0.3"
|
||||||
cached = "0.46"
|
axum = "0.7"
|
||||||
|
axum-extra = "0.9"
|
||||||
|
base64 = "0.22"
|
||||||
|
bytes = "1.7"
|
||||||
|
cached = "0.54"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
compact_str = "0.8"
|
||||||
dotenvy = "0.15"
|
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-core = "0.3"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
headers = "0.3"
|
headers = "0.4"
|
||||||
http = "0.2"
|
http = "1.0"
|
||||||
hyper = "0.14"
|
httpdate = "1"
|
||||||
js-sys = "0.3"
|
hyper = "1.1"
|
||||||
log = "0.4"
|
idna = "1"
|
||||||
miette = "5.9"
|
indexmap = "2.2"
|
||||||
|
itertools = "0.13"
|
||||||
|
kdl = "4"
|
||||||
|
lru = "0.12"
|
||||||
|
miette = "7"
|
||||||
|
nom = "7"
|
||||||
|
nom_locate = "4"
|
||||||
percent-encoding = "2.2"
|
percent-encoding = "2.2"
|
||||||
redis = "0.23"
|
priority-queue = "2.0"
|
||||||
reqwest = "0.11"
|
quick-xml = "0.36"
|
||||||
sea-orm = "0.12"
|
redis = "0.26"
|
||||||
sea-orm-migration = "0.12"
|
regex = "1.9"
|
||||||
|
rmp-serde = "1.3"
|
||||||
|
rand = "0.8"
|
||||||
|
rsa = "0.9"
|
||||||
|
reqwest = "0.12"
|
||||||
|
sea-orm = "1"
|
||||||
|
sea-orm-migration = "1"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "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 }
|
tera = { version = "1", default-features = false }
|
||||||
thiserror = "1"
|
thiserror = "2"
|
||||||
tokio = "1.24"
|
tokio = "1.24"
|
||||||
tokio-util = "0.7"
|
tokio-util = "0.7"
|
||||||
|
tokio-stream = "0.1"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
tower = "0.4"
|
tower = "0.5"
|
||||||
tower-http = "0.4"
|
tower-http = "0.6"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
ts-rs = "7"
|
ts-rs = "7"
|
||||||
|
ulid = "1"
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation = "1.10"
|
||||||
url = "2.3"
|
url = "2.3"
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
wasm-bindgen = "0.2"
|
|
||||||
wasm-bindgen-futures = "0.4"
|
|
||||||
web-sys = "0.3"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnetar_activity_pub = { path = "./ext_activity_pub" }
|
|
||||||
magnetar_core = { path = "./core" }
|
magnetar_core = { path = "./core" }
|
||||||
magnetar_common = { path = "./magnetar_common" }
|
magnetar_common = { path = "./magnetar_common" }
|
||||||
|
magnetar_federation = { path = "./ext_federation" }
|
||||||
|
magnetar_host_meta = { path = "./ext_host_meta" }
|
||||||
magnetar_webfinger = { path = "./ext_webfinger" }
|
magnetar_webfinger = { path = "./ext_webfinger" }
|
||||||
magnetar_nodeinfo = { path = "./ext_nodeinfo" }
|
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" }
|
magnetar_sdk = { path = "./magnetar_sdk" }
|
||||||
|
|
||||||
cached = { workspace = true }
|
cached = { workspace = true }
|
||||||
|
lru = { workspace = true }
|
||||||
|
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
|
|
||||||
async-trait = { workspace = true }
|
axum = { workspace = true, features = ["macros"] }
|
||||||
axum = { workspace = true, features = ["macros", "headers"] }
|
axum-extra = { workspace = true, features = ["typed-header"] }
|
||||||
futures-util = { workspace = true }
|
async-stream = { workspace = true }
|
||||||
headers = { workspace = true }
|
headers = { workspace = true }
|
||||||
hyper = { workspace = true, features = ["full"] }
|
hyper = { workspace = true, features = ["full"] }
|
||||||
|
reqwest = { workspace = true, features = ["hickory-dns"] }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
tokio-stream = { workspace = true, features = ["full"] }
|
||||||
tower = { workspace = true }
|
tower = { workspace = true }
|
||||||
tower-http = { workspace = true, features = ["cors", "trace", "fs"] }
|
tower-http = { workspace = true, features = ["cors", "trace", "fs"] }
|
||||||
|
ulid = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
||||||
reqwest = { workspace = true, features = ["json"] }
|
|
||||||
|
|
||||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
cfg-if = { 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"] }
|
strum = { workspace = true, features = ["derive"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
miette = { workspace = true }
|
|
||||||
|
|
||||||
percent-encoding = { workspace = true }
|
percent-encoding = { workspace = true }
|
||||||
|
|
||||||
|
kdl = { workspace = true }
|
||||||
|
rmp-serde = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
serde_urlencoded = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] }
|
||||||
|
|
||||||
unicode-segmentation = { workspace = true }
|
unicode-segmentation = { workspace = true }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
52
Dockerfile
52
Dockerfile
|
@ -1,20 +1,25 @@
|
||||||
FROM docker.io/alpine:3.18 as build_fe
|
FROM docker.io/rust:1.82-bookworm as build
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
RUN update-ca-certificates
|
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 USER=magnetar
|
||||||
ENV UID=10001
|
ENV UID=10001
|
||||||
|
|
||||||
|
@ -26,13 +31,10 @@ RUN adduser \
|
||||||
--uid "${UID}" \
|
--uid "${UID}" \
|
||||||
"${USER}"
|
"${USER}"
|
||||||
|
|
||||||
WORKDIR /magnetar
|
FROM docker.io/debian:bookworm-slim
|
||||||
|
|
||||||
COPY ./ .
|
RUN apt update
|
||||||
|
RUN apt install -y openssl
|
||||||
RUN cargo build --release --locked --workspace --bins
|
|
||||||
|
|
||||||
FROM docker.io/debian:bullseye-slim
|
|
||||||
|
|
||||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
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
|
||||||
|
|
||||||
WORKDIR /magnetar/fe_calckey/frontend
|
WORKDIR /magnetar/fe_calckey/frontend
|
||||||
COPY --from=build_fe /fe_calckey/frontend/built ./built
|
COPY --from=build /magnetar/fe_calckey/frontend/built ./built
|
||||||
COPY --from=build_fe /fe_calckey/frontend/assets ./assets
|
COPY --from=build /magnetar/fe_calckey/frontend/assets ./assets
|
||||||
COPY --from=build_fe /fe_calckey/frontend/client/assets ./client/assets
|
COPY --from=build /magnetar/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/assets-be ./assets-be
|
||||||
|
|
||||||
WORKDIR /magnetar
|
WORKDIR /magnetar
|
||||||
|
|
||||||
|
@ -59,4 +61,4 @@ USER magnetar:magnetar
|
||||||
EXPOSE 4938/tcp
|
EXPOSE 4938/tcp
|
||||||
EXPOSE 4939/tcp
|
EXPOSE 4939/tcp
|
||||||
|
|
||||||
ENTRYPOINT ["/magnetar/magnetar"]
|
ENTRYPOINT ["/magnetar/magnetar"]
|
||||||
|
|
73
README.md
73
README.md
|
@ -1,33 +1,34 @@
|
||||||
# magnetar
|
# magnetar
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
A social networking server anyone can self-host
|
A social networking server anyone can self-host
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
**This project is in early development.**
|
**This project is in early development.**
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
1. Create a Postgres database
|
1. Create a Postgres database
|
||||||
2. Create a Redis instance
|
2. Create a Redis instance
|
||||||
3. Configure the required options in `config/default.toml`
|
3. Configure the required options in `config/default.toml`
|
||||||
4. Build Magnetar: `cargo build --release --workspace --bins`
|
4. Build Magnetar: `cargo build --release --workspace --bins`
|
||||||
5. Build the Magnetar Calckey frontend: `cd fe_calckey/frontend && pnpm install && pnpm run build`
|
5. Build the Magnetar Calckey frontend: `cd fe_calckey/frontend && pnpm install && pnpm run build`
|
||||||
6. Run Magnetar: `./target/release/magnetar`
|
6. Run Magnetar: `./target/release/magnetar`
|
||||||
7. Build and run [a modified version of Calckey](https://git.astolfo.cool/natty/calckey),
|
7. Build and run [a modified version of Calckey](https://git.astolfo.cool/natty/calckey),
|
||||||
pointed to the same database and Redis
|
pointed to the same database and Redis
|
||||||
8. Start up the Magnetar frontend: `./target/release/magnetar_calckey_fe`
|
8. Start up the Magnetar frontend: `./target/release/magnetar_calckey_fe`
|
||||||
9. Set up a reverse proxy based on `.dev/Caddyfile`
|
9. Set up a reverse proxy based on `.dev/Caddyfile`
|
||||||
|
|
||||||
Read further for additional information.
|
Read further for additional information.
|
||||||
|
|
||||||
## What's inside
|
## What's inside
|
||||||
|
|
||||||
Magnetar builds to two primary binaries:
|
Magnetar builds to two primary binaries:
|
||||||
- magnetar -- The backend
|
|
||||||
- magnetar_calckey_fe -- A web server hosting a self-contained frontend
|
|
||||||
|
|
||||||
The `fe_calckey/frontend` directory contains the
|
- magnetar -- The backend
|
||||||
|
- magnetar_calckey_fe -- A web server hosting a self-contained frontend
|
||||||
|
|
||||||
|
The `fe_calckey/frontend` directory contains the
|
||||||
[Calckey](https://codeberg.org/firefish/firefish)-based web frontend.
|
[Calckey](https://codeberg.org/firefish/firefish)-based web frontend.
|
||||||
|
|
||||||
## Additional dependencies
|
## Additional dependencies
|
||||||
|
@ -43,30 +44,32 @@ Magnetar can be built both as standalone binaries or OCI containers (Podman, Doc
|
||||||
### Native
|
### Native
|
||||||
|
|
||||||
Building the Magnetar backend requires:
|
Building the Magnetar backend requires:
|
||||||
- latest stable Rust toolchain
|
|
||||||
|
- latest stable Rust toolchain
|
||||||
|
|
||||||
Building the included Magnetar/Calckey frontend requires:
|
Building the included Magnetar/Calckey frontend requires:
|
||||||
- latest stable Rust toolchain
|
|
||||||
- Node.js and npm
|
- latest stable Rust toolchain
|
||||||
|
- Node.js and npm
|
||||||
|
|
||||||
#### Steps
|
#### Steps
|
||||||
|
|
||||||
1. Build both the frontend and the backend binaries:
|
1. Build both the frontend and the backend binaries:
|
||||||
```shell
|
```shell
|
||||||
cargo build --release --workspace --bins
|
cargo build --release --workspace --bins
|
||||||
```
|
```
|
||||||
2. Switch to the frontend directory:
|
2. Switch to the frontend directory:
|
||||||
```shell
|
```shell
|
||||||
cd fe_calckey/frontend
|
cd fe_calckey/frontend
|
||||||
```
|
```
|
||||||
3. Install the frontend dependencies:
|
3. Install the frontend dependencies:
|
||||||
```shell
|
```shell
|
||||||
pnpm install
|
pnpm install
|
||||||
```
|
```
|
||||||
4. Build the frontend
|
4. Build the frontend
|
||||||
```shell
|
```shell
|
||||||
pnpm run build
|
pnpm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Container build
|
### Container build
|
||||||
|
|
||||||
|
@ -98,6 +101,6 @@ Alternatively, see the `config/default.toml` file and pass in corresponding envi
|
||||||
|
|
||||||
## Connecting all components
|
## Connecting all components
|
||||||
|
|
||||||
In order to connect all components of magnetar, a router/reverse proxy
|
In order to connect all components of magnetar, a router/reverse proxy
|
||||||
is necessary. See `.dev/Caddyfile` for a template configuration.
|
is necessary. See `.dev/Caddyfile` for a template configuration.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
*
|
*
|
||||||
!.gitignore
|
!.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
|
# Environment variable: MAG_C_BIND_ADDR
|
||||||
# networking.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 ]----------------------------
|
# -----------------------------[ CALCKEY FRONTEND ]----------------------------
|
||||||
|
|
||||||
# [Optional]
|
# [Optional]
|
||||||
|
@ -70,7 +97,6 @@
|
||||||
# -------------------------------[ FEDERATION ]--------------------------------
|
# -------------------------------[ FEDERATION ]--------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------[ BRANDING ]---------------------------------
|
# --------------------------------[ BRANDING ]---------------------------------
|
||||||
|
|
||||||
# [Optional]
|
# [Optional]
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::{
|
||||||
|
borrow::{Borrow, Cow},
|
||||||
|
fmt::Display,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Acct(String);
|
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 {
|
impl Serialize for Acct {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|
|
@ -8,10 +8,12 @@ pub mod acct;
|
||||||
|
|
||||||
pub trait ContentType: Serialize {
|
pub trait ContentType: Serialize {
|
||||||
fn mime_type(&self) -> &'static str;
|
fn mime_type(&self) -> &'static str;
|
||||||
|
|
||||||
|
fn alt_mime_types(&self) -> &[&'static str];
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! content_type {
|
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)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
|
||||||
$visib struct $typ;
|
$visib struct $typ;
|
||||||
|
|
||||||
|
@ -31,13 +33,17 @@ macro_rules! content_type {
|
||||||
fn mime_type(&self) -> &'static str {
|
fn mime_type(&self) -> &'static str {
|
||||||
$expression
|
$expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn alt_mime_types(&self) -> &[&'static str] {
|
||||||
|
&[$($alt),*]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for $typ {
|
impl<'de> Deserialize<'de> for $typ {
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
let content_type = String::deserialize(deserializer)?;
|
let content_type = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
if matches!(content_type.as_ref(), $expression) {
|
if matches!(content_type.as_ref(), $expression $(| $alt)*) {
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::custom(format!(
|
Err(Error::custom(format!(
|
||||||
|
@ -54,9 +60,11 @@ pub mod content_type {
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
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 ContentHtml, "text/html");
|
||||||
content_type!(pub ContentJson, "application/json");
|
content_type!(pub ContentJson, "application/json");
|
||||||
|
content_type!(pub ContentXrdXml, "application/xrd+xml");
|
||||||
content_type!(pub ContentJrdJson, "application/jrd+json");
|
content_type!(pub ContentJrdJson, "application/jrd+json");
|
||||||
content_type!(pub ContentMultipartFormData, "multipart/form-data");
|
content_type!(pub ContentMultipartFormData, "multipart/form-data");
|
||||||
content_type!(pub ContentUrlEncoded, "application/x-www-form-urlencoded");
|
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 RelWebFingerProfilePage, "http://webfinger.net/rel/profile-page");
|
||||||
link_rel!(pub RelSelf, "self");
|
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 RelOStatusSubscribe, "http://ostatus.org/schema/1.0/subscribe");
|
||||||
link_rel!(pub RelNodeInfo20, "http://nodeinfo.diaspora.software/ns/schema/2.0");
|
link_rel!(pub RelNodeInfo20, "http://nodeinfo.diaspora.software/ns/schema/2.0");
|
||||||
link_rel!(pub RelNodeInfo21, "http://nodeinfo.diaspora.software/ns/schema/2.1");
|
link_rel!(pub RelNodeInfo21, "http://nodeinfo.diaspora.software/ns/schema/2.1");
|
||||||
|
@ -139,8 +150,7 @@ where
|
||||||
let dt = data
|
let dt = data
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(Value::as_str)
|
.filter_map(Value::as_str)
|
||||||
.filter_map(|val| T::from_str(val).ok())
|
.find_map(|val| T::from_str(val).ok());
|
||||||
.next();
|
|
||||||
|
|
||||||
if let Some(value) = dt {
|
if let Some(value) = dt {
|
||||||
Ok(ListContaining(value))
|
Ok(ListContaining(value))
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
|
@ -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]
|
[package]
|
||||||
name = "magnetar_activity_pub"
|
name = "magnetar_activity_streams"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
@ -9,11 +9,14 @@ crate-type = ["rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
|
either = { workspace = true }
|
||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
url = { workspace = true, features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[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};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct LinkMention;
|
pub struct LinkMention {}
|
||||||
|
|
||||||
def_ld!("https://www.w3.org/ns/activitystreams#Mention", 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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{def_ld, ObjectSlot};
|
||||||
|
use crate::object::ObjectLinkUnion;
|
||||||
|
use crate::OneOrMore;
|
||||||
|
|
||||||
pub mod link_types;
|
pub mod link_types;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct Link {
|
pub struct Link<'a> {
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#href")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#href")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub href: Option<String>,
|
pub href: Option<String>,
|
||||||
|
@ -37,8 +38,7 @@ pub struct Link {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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);
|
def_ld!("https://www.w3.org/ns/activitystreams#Link", Link<'_>);
|
||||||
ld_document!(RefLink, 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::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::{def_ld, ObjectSlot};
|
||||||
|
use crate::object::{Collection, ObjectLinkUnion, OrderedCollection};
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ActorActivityPubProps {
|
pub struct ActorActivityPubProps<'a> {
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#inbox")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#inbox")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#outbox")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#following")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#followers")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#liked")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#shares")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#preferredUsername")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -34,35 +36,30 @@ pub struct ActorActivityPubProps {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#endpoints")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#endpoints")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub endpoints: Option<String>,
|
pub endpoints: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ActorApplication;
|
pub struct ActorApplication {}
|
||||||
|
|
||||||
def_ld!("Application", ActorApplication, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Application", ActorApplication, base as Object);
|
||||||
ld_document!(RefActorApplication, ActorApplication);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ActorGroup;
|
pub struct ActorGroup {}
|
||||||
|
|
||||||
def_ld!("Group", ActorGroup, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Group", ActorGroup, base as Object);
|
||||||
ld_document!(RefActorGroup, ActorGroup);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ActorOrganization;
|
pub struct ActorOrganization {}
|
||||||
|
|
||||||
def_ld!("Organization", ActorOrganization, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Organization", ActorOrganization, base as Object);
|
||||||
ld_document!(RefActorOrganization, ActorOrganization);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ActorPerson;
|
pub struct ActorPerson {}
|
||||||
|
|
||||||
def_ld!("Person", ActorPerson, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Person", ActorPerson, base as Object);
|
||||||
ld_document!(RefActorPerson, ActorPerson);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ActorService;
|
pub struct ActorService {}
|
||||||
|
|
||||||
def_ld!("Service", ActorService, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Service", ActorService, base as Object);
|
||||||
ld_document!(RefActorService, ActorService);
|
|
|
@ -1,28 +1,30 @@
|
||||||
use crate::link::Link;
|
use std::collections::HashMap;
|
||||||
use crate::object::object_types::RefObjectImageLinkUnion;
|
|
||||||
use crate::{def_ld, Id};
|
|
||||||
use crate::{ld_document, ld_union, Base, LinkOrUrl, ObjectRaw, ObjectSingle, OneOrMore};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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 activity;
|
||||||
pub mod actor;
|
pub mod actor;
|
||||||
pub mod object_types;
|
pub mod object_types;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct Object {
|
pub struct Object<'a> {
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#attachment")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#attachment")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#attributedTo")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#audience")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#content")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -34,7 +36,7 @@ pub struct Object {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#context")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#context")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#name")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -50,27 +52,27 @@ pub struct Object {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#generator")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#generator")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#icon")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#image")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#inReplyTo")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#location")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#published")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -78,7 +80,7 @@ pub struct Object {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#replies")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#replies")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#startTime")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -94,7 +96,7 @@ pub struct Object {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#tag")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#tag")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#updated")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -102,23 +104,23 @@ pub struct Object {
|
||||||
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#url")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#url")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#to")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#bto")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#cc")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#bcc")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#mediaType")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -131,13 +133,11 @@ pub struct Object {
|
||||||
// ActivityPub
|
// ActivityPub
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#source")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#source")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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);
|
def_ld!("https://www.w3.org/ns/activitystreams#Object", Object<'_>);
|
||||||
ld_document!(RefObject, Object);
|
|
||||||
ld_union!(
|
ld_union!(
|
||||||
RefObjectLinkUnion,
|
|
||||||
ObjectLinkUnion,
|
ObjectLinkUnion,
|
||||||
object_props as Object,
|
object_props as Object,
|
||||||
link_props as Link
|
link_props as Link
|
||||||
|
@ -170,9 +170,7 @@ def_ld!(
|
||||||
"https://www.w3.org/ns/activitystreams#Collection",
|
"https://www.w3.org/ns/activitystreams#Collection",
|
||||||
Collection
|
Collection
|
||||||
);
|
);
|
||||||
ld_document!(RefCollection, Collection);
|
|
||||||
ld_union!(
|
ld_union!(
|
||||||
RefCollectionLinkUnion,
|
|
||||||
CollectionLinkUnion,
|
CollectionLinkUnion,
|
||||||
collection_props as Collection,
|
collection_props as Collection,
|
||||||
link_props as Link
|
link_props as Link
|
||||||
|
@ -189,7 +187,6 @@ def_ld!(
|
||||||
"https://www.w3.org/ns/activitystreams#OrderedCollection",
|
"https://www.w3.org/ns/activitystreams#OrderedCollection",
|
||||||
OrderedCollection
|
OrderedCollection
|
||||||
);
|
);
|
||||||
ld_document!(RefOrderedCollection, OrderedCollection);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct CollectionPage {
|
pub struct CollectionPage {
|
||||||
|
@ -210,10 +207,7 @@ def_ld!(
|
||||||
"https://www.w3.org/ns/activitystreams#CollectionPage",
|
"https://www.w3.org/ns/activitystreams#CollectionPage",
|
||||||
CollectionPage
|
CollectionPage
|
||||||
);
|
);
|
||||||
ld_document!(RefCollectionPage, CollectionPage);
|
|
||||||
|
|
||||||
ld_union!(
|
ld_union!(
|
||||||
RefCollectionPageLinkUnion,
|
|
||||||
CollectionPageLinkUnion,
|
CollectionPageLinkUnion,
|
||||||
page_props as CollectionPage,
|
page_props as CollectionPage,
|
||||||
link_props as Link
|
link_props as Link
|
||||||
|
@ -230,4 +224,3 @@ def_ld!(
|
||||||
"https://www.w3.org/ns/activitystreams#OrderedCollectionPage",
|
"https://www.w3.org/ns/activitystreams#OrderedCollectionPage",
|
||||||
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 chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectRelationship {
|
pub struct ObjectRelationship<'a> {
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#subject")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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(rename = "https://www.w3.org/ns/activitystreams#relationship")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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);
|
def_ld!("https://www.w3.org/ns/activitystreams#Relationship", ObjectRelationship, base as Object);
|
||||||
ld_document!(RefObjectRelationship, ObjectRelationship);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectArticle;
|
pub struct ObjectArticle {}
|
||||||
|
|
||||||
def_ld!("Article", ObjectArticle, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Article", ObjectArticle, base as Object);
|
||||||
ld_document!(RefObjectArticle, ObjectArticle);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectDocument;
|
pub struct ObjectDocument {}
|
||||||
|
|
||||||
def_ld!("Document", ObjectDocument, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Document", ObjectDocument, base as Object);
|
||||||
ld_document!(RefObjectDocument, ObjectDocument);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectAudio;
|
pub struct ObjectAudio {}
|
||||||
|
|
||||||
def_ld!("Audio", ObjectAudio, base as ObjectDocument);
|
def_ld!("https://www.w3.org/ns/activitystreams#Audio", ObjectAudio, base as ObjectDocument);
|
||||||
ld_document!(RefObjectAudio, ObjectAudio);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectImage;
|
pub struct ObjectImage {}
|
||||||
|
|
||||||
def_ld!("Image", ObjectImage, base as ObjectDocument);
|
def_ld!("https://www.w3.org/ns/activitystreams#Image", ObjectImage, base as ObjectDocument);
|
||||||
ld_document!(RefObjectImage, ObjectImage);
|
|
||||||
ld_union!(
|
ld_union!(
|
||||||
RefObjectImageLinkUnion,
|
|
||||||
ObjectImageLinkUnion,
|
ObjectImageLinkUnion,
|
||||||
image_props as ObjectImage,
|
image_props as ObjectImage,
|
||||||
link_props as Link
|
link_props as Link
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectVideo;
|
pub struct ObjectVideo {}
|
||||||
|
|
||||||
def_ld!("Video", ObjectVideo, base as ObjectDocument);
|
def_ld!("https://www.w3.org/ns/activitystreams#Video", ObjectVideo, base as ObjectDocument);
|
||||||
ld_document!(RefObjectVideo, ObjectVideo);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectNote;
|
pub struct ObjectNote {}
|
||||||
|
|
||||||
def_ld!("Note", ObjectNote, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Note", ObjectNote, base as Object);
|
||||||
ld_document!(RefObjectNote, ObjectNote);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectPage;
|
pub struct ObjectPage {}
|
||||||
|
|
||||||
def_ld!("Page", ObjectPage, base as ObjectDocument);
|
def_ld!("https://www.w3.org/ns/activitystreams#Page", ObjectPage, base as ObjectDocument);
|
||||||
ld_document!(RefObjectPage, ObjectPage);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectEvent;
|
pub struct ObjectEvent {}
|
||||||
|
|
||||||
def_ld!("Event", ObjectEvent, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Event", ObjectEvent, base as Object);
|
||||||
ld_document!(RefObjectEvent, ObjectEvent);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectPlace {
|
pub struct ObjectPlace {
|
||||||
|
@ -103,18 +94,16 @@ pub struct ObjectPlace {
|
||||||
pub units: Option<String>,
|
pub units: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
def_ld!("Place", ObjectPlace, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Place", ObjectPlace, base as Object);
|
||||||
ld_document!(RefObjectPlace, ObjectPlace);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectProfile {
|
pub struct ObjectProfile<'a> {
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#describes")]
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#describes")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[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);
|
def_ld!("https://www.w3.org/ns/activitystreams#Profile", ObjectProfile, base as Object);
|
||||||
ld_document!(RefObjectProfile, ObjectProfile);
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct ObjectTombstone {
|
pub struct ObjectTombstone {
|
||||||
|
@ -127,5 +116,4 @@ pub struct ObjectTombstone {
|
||||||
pub deleted: Option<DateTime<Utc>>,
|
pub deleted: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
def_ld!("Tombstone", ObjectTombstone, base as Object);
|
def_ld!("https://www.w3.org/ns/activitystreams#Tombstone", ObjectTombstone, base as Object);
|
||||||
ld_document!(RefObjectTombstone, ObjectTombstone);
|
|
|
@ -1,16 +1,9 @@
|
||||||
use crate::{OneOrMore, Resolvable};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[macro_use]
|
use crate::OneOrMore;
|
||||||
pub mod account_move;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod block;
|
pub mod block;
|
||||||
#[macro_use]
|
|
||||||
pub mod flag;
|
|
||||||
#[macro_use]
|
|
||||||
pub mod poll;
|
|
||||||
#[macro_use]
|
|
||||||
pub mod tag;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum RecipeError {
|
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,58 @@
|
||||||
|
[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"] }
|
||||||
|
|
||||||
|
rand = { workspace = true }
|
||||||
|
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,483 @@
|
||||||
|
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::Write;
|
||||||
|
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()
|
||||||
|
.fold(String::new(), |mut acc, (k, v)| {
|
||||||
|
writeln!(&mut acc, "{}: {}", k.as_ref(), v).unwrap();
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
|
||||||
|
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.trim_end().as_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_static("signature"),
|
||||||
|
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_bytes: Vec<u8>,
|
||||||
|
) -> Result<String, Self::Error> {
|
||||||
|
let url = url.parse()?;
|
||||||
|
// 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_static("digest"),
|
||||||
|
HeaderValue::try_from(digest_base64)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
headers.insert(
|
||||||
|
HeaderName::from_static("signature"),
|
||||||
|
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,163 @@
|
||||||
|
use async_stream::stream;
|
||||||
|
use futures_util::{stream::StreamExt, Stream, TryStreamExt};
|
||||||
|
use headers::UserAgent;
|
||||||
|
use hyper::body::Bytes;
|
||||||
|
use reqwest::{redirect::Policy, Client, RequestBuilder};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
|
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))
|
||||||
|
.timeout(Duration::from_secs(timeout_seconds))
|
||||||
|
.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> {
|
||||||
|
self.send_stream()
|
||||||
|
.await?
|
||||||
|
.try_fold(Vec::new(), |mut acc, b| async move {
|
||||||
|
acc.extend_from_slice(&b);
|
||||||
|
Ok(acc)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
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::{RandomizedSigner, 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_with_rng(&mut rand::thread_rng(), message)).into_vec())
|
||||||
|
}
|
||||||
|
(Self::RsaSha512(key), SigningAlgorithm::Hs2019) => {
|
||||||
|
Ok(Box::<[u8]>::from(key.sign_with_rng(&mut rand::thread_rng(), 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: Vec<u8>,
|
||||||
|
) -> 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]
|
[package]
|
||||||
name = "magnetar_calckey_model"
|
name = "magnetar_model"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
|
@ -8,20 +8,26 @@ crate-type = ["rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ck = { path = "./entity_ck" }
|
ck = { path = "./entity_ck" }
|
||||||
ext_calckey_model_migration = { path = "./migration" }
|
ext_model_migration = { path = "./migration" }
|
||||||
|
|
||||||
magnetar_common = { path = "../magnetar_common" }
|
magnetar_common = { path = "../magnetar_common" }
|
||||||
|
magnetar_sdk = { path = "../magnetar_sdk" }
|
||||||
|
|
||||||
dotenvy = { workspace = true}
|
dotenvy = { workspace = true }
|
||||||
futures-core = { workspace = true }
|
futures-core = { workspace = true }
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
tokio-util = { workspace = true}
|
tokio-util = { workspace = true }
|
||||||
redis = { workspace = true, features = ["tokio-comp", "json", "serde_json"]}
|
redis = { workspace = true, features = ["tokio-comp", "json", "serde_json"] }
|
||||||
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls", "macros"] }
|
sea-orm = { workspace = true, features = [
|
||||||
|
"sqlx-postgres",
|
||||||
|
"runtime-tokio-rustls",
|
||||||
|
"macros",
|
||||||
|
] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
tracing = { 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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 super::sea_orm_active_enums::AntennaSrcEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
@ -27,8 +27,6 @@ pub struct Model {
|
||||||
pub case_sensitive: bool,
|
pub case_sensitive: bool,
|
||||||
#[sea_orm(column_name = "withReplies")]
|
#[sea_orm(column_name = "withReplies")]
|
||||||
pub with_replies: bool,
|
pub with_replies: bool,
|
||||||
#[sea_orm(column_name = "userGroupJoiningId")]
|
|
||||||
pub user_group_joining_id: Option<String>,
|
|
||||||
pub users: Vec<String>,
|
pub users: Vec<String>,
|
||||||
#[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")]
|
#[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")]
|
||||||
pub exclude_keywords: Json,
|
pub exclude_keywords: Json,
|
||||||
|
@ -48,14 +46,6 @@ pub enum Relation {
|
||||||
on_delete = "Cascade"
|
on_delete = "Cascade"
|
||||||
)]
|
)]
|
||||||
User,
|
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(
|
#[sea_orm(
|
||||||
belongs_to = "super::user_list::Entity",
|
belongs_to = "super::user_list::Entity",
|
||||||
from = "Column::UserListId",
|
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 {
|
impl Related<super::user_list::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::UserList.def()
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -65,8 +65,6 @@ pub enum Relation {
|
||||||
on_delete = "SetNull"
|
on_delete = "SetNull"
|
||||||
)]
|
)]
|
||||||
DriveFolder,
|
DriveFolder,
|
||||||
#[sea_orm(has_many = "super::page::Entity")]
|
|
||||||
Page,
|
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::user::Entity",
|
belongs_to = "super::user::Entity",
|
||||||
from = "Column::UserId",
|
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 {
|
impl Related<super::user::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::User.def()
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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::MetaSensitivemediadetectionEnum;
|
||||||
use super::sea_orm_active_enums::MetaSensitivemediadetectionsensitivityEnum;
|
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;
|
pub mod prelude;
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@ pub mod drive_folder;
|
||||||
pub mod emoji;
|
pub mod emoji;
|
||||||
pub mod follow_request;
|
pub mod follow_request;
|
||||||
pub mod following;
|
pub mod following;
|
||||||
pub mod gallery_like;
|
|
||||||
pub mod gallery_post;
|
|
||||||
pub mod hashtag;
|
pub mod hashtag;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod meta;
|
pub mod meta;
|
||||||
|
@ -36,8 +34,6 @@ pub mod note_thread_muting;
|
||||||
pub mod note_unread;
|
pub mod note_unread;
|
||||||
pub mod note_watching;
|
pub mod note_watching;
|
||||||
pub mod notification;
|
pub mod notification;
|
||||||
pub mod page;
|
|
||||||
pub mod page_like;
|
|
||||||
pub mod password_reset_request;
|
pub mod password_reset_request;
|
||||||
pub mod poll;
|
pub mod poll;
|
||||||
pub mod poll_vote;
|
pub mod poll_vote;
|
||||||
|
@ -52,10 +48,6 @@ pub mod signin;
|
||||||
pub mod sw_subscription;
|
pub mod sw_subscription;
|
||||||
pub mod used_username;
|
pub mod used_username;
|
||||||
pub mod user;
|
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_ip;
|
||||||
pub mod user_keypair;
|
pub mod user_keypair;
|
||||||
pub mod user_list;
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 super::sea_orm_active_enums::MutedNoteReasonEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 super::sea_orm_active_enums::NoteVisibilityEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
@ -60,6 +60,8 @@ pub struct Model {
|
||||||
pub thread_id: Option<String>,
|
pub thread_id: Option<String>,
|
||||||
#[sea_orm(column_name = "updatedAt")]
|
#[sea_orm(column_name = "updatedAt")]
|
||||||
pub updated_at: Option<DateTimeWithTimeZone>,
|
pub updated_at: Option<DateTimeWithTimeZone>,
|
||||||
|
pub is_quote: Option<bool>,
|
||||||
|
pub is_renote: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 super::sea_orm_active_enums::NotificationTypeEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
@ -24,8 +24,6 @@ pub struct Model {
|
||||||
#[sea_orm(column_name = "followRequestId")]
|
#[sea_orm(column_name = "followRequestId")]
|
||||||
pub follow_request_id: Option<String>,
|
pub follow_request_id: Option<String>,
|
||||||
pub r#type: NotificationTypeEnum,
|
pub r#type: NotificationTypeEnum,
|
||||||
#[sea_orm(column_name = "userGroupInvitationId")]
|
|
||||||
pub user_group_invitation_id: Option<String>,
|
|
||||||
#[sea_orm(column_name = "customBody")]
|
#[sea_orm(column_name = "customBody")]
|
||||||
pub custom_body: Option<String>,
|
pub custom_body: Option<String>,
|
||||||
#[sea_orm(column_name = "customHeader")]
|
#[sea_orm(column_name = "customHeader")]
|
||||||
|
@ -78,14 +76,6 @@ pub enum Relation {
|
||||||
on_delete = "Cascade"
|
on_delete = "Cascade"
|
||||||
)]
|
)]
|
||||||
User1,
|
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 {
|
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 {}
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 super::sea_orm_active_enums::PollNotevisibilityEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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::abuse_user_report::Entity as AbuseUserReport;
|
||||||
pub use super::access_token::Entity as AccessToken;
|
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::emoji::Entity as Emoji;
|
||||||
pub use super::follow_request::Entity as FollowRequest;
|
pub use super::follow_request::Entity as FollowRequest;
|
||||||
pub use super::following::Entity as Following;
|
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::hashtag::Entity as Hashtag;
|
||||||
pub use super::instance::Entity as Instance;
|
pub use super::instance::Entity as Instance;
|
||||||
pub use super::meta::Entity as Meta;
|
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_unread::Entity as NoteUnread;
|
||||||
pub use super::note_watching::Entity as NoteWatching;
|
pub use super::note_watching::Entity as NoteWatching;
|
||||||
pub use super::notification::Entity as Notification;
|
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::password_reset_request::Entity as PasswordResetRequest;
|
||||||
pub use super::poll::Entity as Poll;
|
pub use super::poll::Entity as Poll;
|
||||||
pub use super::poll_vote::Entity as PollVote;
|
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::sw_subscription::Entity as SwSubscription;
|
||||||
pub use super::used_username::Entity as UsedUsername;
|
pub use super::used_username::Entity as UsedUsername;
|
||||||
pub use super::user::Entity as User;
|
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_ip::Entity as UserIp;
|
||||||
pub use super::user_keypair::Entity as UserKeypair;
|
pub use super::user_keypair::Entity as UserKeypair;
|
||||||
pub use super::user_list::Entity as UserList;
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 super::sea_orm_active_enums::RelayStatusEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -8,8 +8,6 @@ use serde::{Deserialize, Serialize};
|
||||||
pub enum AntennaSrcEnum {
|
pub enum AntennaSrcEnum {
|
||||||
#[sea_orm(string_value = "all")]
|
#[sea_orm(string_value = "all")]
|
||||||
All,
|
All,
|
||||||
#[sea_orm(string_value = "group")]
|
|
||||||
Group,
|
|
||||||
#[sea_orm(string_value = "home")]
|
#[sea_orm(string_value = "home")]
|
||||||
Home,
|
Home,
|
||||||
#[sea_orm(string_value = "instances")]
|
#[sea_orm(string_value = "instances")]
|
||||||
|
@ -100,14 +98,10 @@ pub enum NotificationTypeEnum {
|
||||||
Follow,
|
Follow,
|
||||||
#[sea_orm(string_value = "followRequestAccepted")]
|
#[sea_orm(string_value = "followRequestAccepted")]
|
||||||
FollowRequestAccepted,
|
FollowRequestAccepted,
|
||||||
#[sea_orm(string_value = "groupInvited")]
|
|
||||||
GroupInvited,
|
|
||||||
#[sea_orm(string_value = "mention")]
|
#[sea_orm(string_value = "mention")]
|
||||||
Mention,
|
Mention,
|
||||||
#[sea_orm(string_value = "pollEnded")]
|
#[sea_orm(string_value = "pollEnded")]
|
||||||
PollEnded,
|
PollEnded,
|
||||||
#[sea_orm(string_value = "pollVote")]
|
|
||||||
PollVote,
|
|
||||||
#[sea_orm(string_value = "quote")]
|
#[sea_orm(string_value = "quote")]
|
||||||
Quote,
|
Quote,
|
||||||
#[sea_orm(string_value = "reaction")]
|
#[sea_orm(string_value = "reaction")]
|
||||||
|
@ -120,20 +114,6 @@ pub enum NotificationTypeEnum {
|
||||||
Reply,
|
Reply,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, Serialize, Deserialize)]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -173,35 +153,3 @@ pub enum UserProfileFfvisibilityEnum {
|
||||||
#[sea_orm(string_value = "public")]
|
#[sea_orm(string_value = "public")]
|
||||||
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -106,10 +106,6 @@ pub enum Relation {
|
||||||
DriveFile1,
|
DriveFile1,
|
||||||
#[sea_orm(has_many = "super::drive_folder::Entity")]
|
#[sea_orm(has_many = "super::drive_folder::Entity")]
|
||||||
DriveFolder,
|
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")]
|
#[sea_orm(has_many = "super::meta::Entity")]
|
||||||
Meta,
|
Meta,
|
||||||
#[sea_orm(has_many = "super::moderation_log::Entity")]
|
#[sea_orm(has_many = "super::moderation_log::Entity")]
|
||||||
|
@ -128,10 +124,6 @@ pub enum Relation {
|
||||||
NoteUnread,
|
NoteUnread,
|
||||||
#[sea_orm(has_many = "super::note_watching::Entity")]
|
#[sea_orm(has_many = "super::note_watching::Entity")]
|
||||||
NoteWatching,
|
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")]
|
#[sea_orm(has_many = "super::password_reset_request::Entity")]
|
||||||
PasswordResetRequest,
|
PasswordResetRequest,
|
||||||
#[sea_orm(has_many = "super::poll_vote::Entity")]
|
#[sea_orm(has_many = "super::poll_vote::Entity")]
|
||||||
|
@ -144,14 +136,6 @@ pub enum Relation {
|
||||||
Signin,
|
Signin,
|
||||||
#[sea_orm(has_many = "super::sw_subscription::Entity")]
|
#[sea_orm(has_many = "super::sw_subscription::Entity")]
|
||||||
SwSubscription,
|
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")]
|
#[sea_orm(has_one = "super::user_keypair::Entity")]
|
||||||
UserKeypair,
|
UserKeypair,
|
||||||
#[sea_orm(has_many = "super::user_list::Entity")]
|
#[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 {
|
impl Related<super::meta::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Meta.def()
|
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 {
|
impl Related<super::password_reset_request::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::PasswordResetRequest.def()
|
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 {
|
impl Related<super::user_keypair::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::UserKeypair.def()
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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 sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
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